django-taggit not working when using UUID
Asked Answered
O

3

6

I have gone through the customization documentation here https://django-taggit.readthedocs.io/en/latest/custom_tagging.html#genericuuidtaggeditembase

I am using the following code, when I save the product through django admin, tables are getting populated properly but when I am reading a product, tags are coming as None

catalog/models.py

from django.db import models
from django.db.models import ImageField
from django.contrib.auth.models import User
from django.utils.translation import ugettext_lazy as _

from taggit.managers import TaggableManager
from taggit.models import GenericUUIDTaggedItemBase, TaggedItemBase

from common.models import ModelBase
from customer.models import ApplicationUser
from order_quick.settings import APPLICATION_CURRENCY_SYMBOL


class TaggedItem(GenericUUIDTaggedItemBase, TaggedItemBase):
    class Meta:
        verbose_name = _("Tag")
        verbose_name_plural = _("Tags")


class Product(ModelBase):
    supplier = models.ForeignKey(ApplicationUser, on_delete=models.DO_NOTHING)
    name = models.CharField(max_length=255)
    description = models.CharField(max_length=255)
    image = ImageField(upload_to='images/products/', blank=True, null=True)
    cost_price = models.DecimalField(max_digits=9,
                                     decimal_places=2,
                                     verbose_name="Cost Price " + "(" + APPLICATION_CURRENCY_SYMBOL + ")")
    selling_price = models.DecimalField(max_digits=9,
                                        decimal_places=2,
                                        verbose_name="Selling Price " + "(" + APPLICATION_CURRENCY_SYMBOL + ")")
    is_active = models.BooleanField(default=True)
    tags = TaggableManager(through=TaggedItem)

    def __str__(self):
        return "{0}".format(self.name)

common/models.py

import uuid
from enum import Enum

from django.db import models


class ModelBase(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True

The code above create a new table 'catalog_taggeditem' in my django application called 'catalog'. There is also the default table from django-taggit called 'taggit_taggeditem'. Seems like while reading, it can't connect the dots. I am not sure, what am I missing, there are no errors.

Thanks for your help.

-----------------------UPDATE--------------------

Product.objects.first().tags.first()
Traceback (most recent call last):
  File "/home/chirdeep/envs/order-quick/lib/python3.6/site-packages/django/db/backends/utils.py", line 84, in _execute
    return self.cursor.execute(sql, params)
psycopg2.ProgrammingError: operator does not exist: character varying = uuid
LINE 1: ... = 'product' AND "catalog_taggeditem"."object_id" = '903cda0...
                                                             ^
HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
Outguard answered 26/5, 2019 at 22:28 Comment(8)
Can you include the full source of your model - including the imports?Vachel
Where is the name catalog coming from for catalog_taggeditem? Can you see the tags as you would expect in the admin?Vachel
@Vachel catalog_taggeditem is coming from catalog app which is where TaggedItem is defined in models.py. I can see the data in catalog_taggeditem table, seems like while reading django-taggit cant establish the link between taggit_tag and catalog_taggeditem.Outguard
Are you sure you have run all migrations? It seems that the "catalog_taggeditem"."object_id" is using postgres varchar type instead of uuid data type.Sucking
If your database types somehow have been mixed up, it's possible to convert a column with a SQL query. Something like ALTER TABLE catalog_taggeditem ALTER COLUMN object_id TYPE uuid USING object_id::uuid;, perhaps. (It might be some other column that is mixed up)Sucking
@HåkenLid Altering the table worked for me, seemed cleaner. Please answer the question and I will mark as accepted. Although Igor's solution should also work.Outguard
@ChirdeepTomar That's nice. I've added an answer with the SQL solution.Sucking
Can you give exact versions (django, taggit, psycopg2) @ChirdeepTomar . Ran your example with: ` Django==2.2.2 django-taggit==1.1.0 Pillow==6.0.0 psycopg2==2.8.2 pytz==2019.1 sqlparse==0.3.0 ` And was able to make a query with no errorTelstar
M
0

The error you are getting comes from the postgres adapter. For some reason the object_id column seems to be of type varying (varchar) instead of the expected uuid.

psycopg2.ProgrammingError: operator does not exist: character varying = uuid
LINE 1: ... = 'product' AND "catalog_taggeditem"."object_id" = '903cda0...

Postgres has a native UUID datatype which Django has supported for a long time, so I don't know how this could have happened. Maybe an incomplete migration?

In any case, you can convert an entire database column to a new type with SQL. Assuming the problem is only with the column mentioned in this specific error, something like this can work.

ALTER TABLE catalog_taggeditem ALTER COLUMN object_id TYPE uuid USING object_id::uuid;

You can use Django's special RunSQL migration operation for this.

migrations.RunSQL(
    sql='''
        ALTER TABLE catalog_taggeditem 
        ALTER COLUMN object_id TYPE uuid 
        USING object_id::uuid;''',
    reverse_sql='''
        ALTER TABLE catalog_taggeditem 
        ALTER COLUMN object_id TYPE varchar(32) 
        USING object_id::varchar(32);''',
)
Mccowan answered 3/6, 2019 at 7:14 Comment(0)
J
3

I had similar issues, when used GFKs. Adding explicit types cast helped in my case. I'm not 100% sure it will work, but try to do this in console:

psql -d <your_database>
create cast (uuid as varchar) with inout as implicit;
\q

If it will help, you should also do the same for database template1 (which is used as template for new databases creation — it will give you proper setup for the databases created for Django's unittests).

Joyajoyan answered 2/6, 2019 at 18:1 Comment(1)
Good idea. I think this should work, even though implicit type casts ususally is a big no-no both with python and postgresql. If you figure out a cleaner solution later, you can remove this type cast with the DROP CAST directive.Sucking
V
0

I'm not able to reproduce your issue. See the source I'm using here: https://github.com/jayhale/so-django-taggit

Tags are successfully created and are retrievable:

$ python manage.py shell
Python 3.7.2 (default, Dec 27 2018, 07:35:06) 
[Clang 10.0.0 (clang-1000.11.45.5)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from taggit_app.models import Product
>>> p = Product()
>>> p.save()
>>> p
<Product: Product object (71a56d92-13eb-4d7d-9e67-46c9cd1daa19)>
>>> p.tags.add('a', 'b', 'c')
>>> p.tags.all()
<QuerySet [<Tag: c>, <Tag: b>, <Tag: a>]>
Vachel answered 2/6, 2019 at 14:9 Comment(1)
Seems like an issue with Postgres and Django GFK.Outguard
M
0

The error you are getting comes from the postgres adapter. For some reason the object_id column seems to be of type varying (varchar) instead of the expected uuid.

psycopg2.ProgrammingError: operator does not exist: character varying = uuid
LINE 1: ... = 'product' AND "catalog_taggeditem"."object_id" = '903cda0...

Postgres has a native UUID datatype which Django has supported for a long time, so I don't know how this could have happened. Maybe an incomplete migration?

In any case, you can convert an entire database column to a new type with SQL. Assuming the problem is only with the column mentioned in this specific error, something like this can work.

ALTER TABLE catalog_taggeditem ALTER COLUMN object_id TYPE uuid USING object_id::uuid;

You can use Django's special RunSQL migration operation for this.

migrations.RunSQL(
    sql='''
        ALTER TABLE catalog_taggeditem 
        ALTER COLUMN object_id TYPE uuid 
        USING object_id::uuid;''',
    reverse_sql='''
        ALTER TABLE catalog_taggeditem 
        ALTER COLUMN object_id TYPE varchar(32) 
        USING object_id::varchar(32);''',
)
Mccowan answered 3/6, 2019 at 7:14 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.