django-taggit on models with UUID as pk throwing out of range on save
Asked Answered
W

2

6

I have models that uses UUID as its PK

class Foo(models.Model):        
    foo_id = models.UUIDField(  
        primary_key=True,             
        default=uuid.uuid4,           
        editable=False                
    )  
    tags = TaggableManager()        

When I go and try to add a new tag

f = Foo.objects.latest('pk')
f.tags.add("testing")

I get DataError: integer out of range

When I import pdb on the cursor to view the SQL going in I see this.

(Pdb) params                                                                                                              
(1, 287082253891563438098836942573405313042, 9)                                                                           
(Pdb) sql                                                                                                                 
'INSERT INTO "taggit_taggeditem" ("tag_id", "object_id", "content_type_id") VALUES (%s, %s, %s) RETURNING "taggit_taggedit
m"."id"'    

That long integer (287082253891563438098836942573405313042) trying to be insterted is obvsiouly the cause for the error. This number is the int of the UUID for foo_id

In [6]: foo.foo_id.int                      
Out[6]: 287082253891563438098836942573405313042  

Is there something I can set to allow django-taggit to play nicely with contenttypes and UUID?

Watchcase answered 28/7, 2015 at 17:24 Comment(1)
I ended up forking a copy of taggit and modifying the Model to use a UUID so it would be compatible with my UUID field. Note you need to change the migration file as well before syncing your DBWatchcase
E
3

Here's an answer based on Austin's comment.

In django-taggit, references to tagged models are stored in a model named GenericTaggedItemBase under a field called object_id. The object_id field is hardcoded to models.IntegerField. So it's not possible to tag models having UUID primary keys. The code is located here.

If all your models that need to be tagged are of the same type (in this case, models.UUIDField), then you can set object_id's type to models.UUIDField.

Here are the changes that have to be made, assuming you're using virtualenvwrapper

  1. Locate the taggit package in the site packages folder. ~/virtualenvs/<your_virtualenv>/lib/<python_version>/site-packages/taggit

  2. Copy the taggit directory into your project.

  3. Delete the taggit directory from site-packages

  4. In the models.py file in taggit, replace

object_id = models.IntegerField(verbose_name=_('Object id'), db_index=True) with

object_id = models.UUIDField(verbose_name=_('Object id'), db_index=True)
  1. Migrate taggit.

python manage.py makemigrations taggit

python manage.py migrate

Extrovert answered 18/11, 2015 at 4:0 Comment(1)
When copying the taggit package into my own directory I just updated the migration rather then running makemigrations. They either need to be updated or removed then make migrationsWatchcase
B
15

I'd like to extend @Pramod response, that was very helpful for me to find the right answer:

Taggit has another class that allows to change the behavior of the TaggedItem

Here is a snippet of how to implement this solution:

from django.db import models
from django.utils.translation import ugettext_lazy as _

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

class UUIDTaggedItem(GenericUUIDTaggedItemBase, TaggedItemBase):
    # If you only inherit GenericUUIDTaggedItemBase, you need to define
    # a tag field. e.g.
    # tag = models.ForeignKey(Tag, related_name="uuid_tagged_items", on_delete=models.CASCADE)

    class Meta:
        verbose_name = _("Tag")
        verbose_name_plural = _("Tags")

class Food(models.Model):
    id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
    # ... fields here

    tags = TaggableManager(through=UUIDTaggedItem)

source: http://django-taggit.readthedocs.io/en/latest/custom_tagging.html#genericuuidtaggeditembase

Byre answered 5/6, 2016 at 2:21 Comment(0)
E
3

Here's an answer based on Austin's comment.

In django-taggit, references to tagged models are stored in a model named GenericTaggedItemBase under a field called object_id. The object_id field is hardcoded to models.IntegerField. So it's not possible to tag models having UUID primary keys. The code is located here.

If all your models that need to be tagged are of the same type (in this case, models.UUIDField), then you can set object_id's type to models.UUIDField.

Here are the changes that have to be made, assuming you're using virtualenvwrapper

  1. Locate the taggit package in the site packages folder. ~/virtualenvs/<your_virtualenv>/lib/<python_version>/site-packages/taggit

  2. Copy the taggit directory into your project.

  3. Delete the taggit directory from site-packages

  4. In the models.py file in taggit, replace

object_id = models.IntegerField(verbose_name=_('Object id'), db_index=True) with

object_id = models.UUIDField(verbose_name=_('Object id'), db_index=True)
  1. Migrate taggit.

python manage.py makemigrations taggit

python manage.py migrate

Extrovert answered 18/11, 2015 at 4:0 Comment(1)
When copying the taggit package into my own directory I just updated the migration rather then running makemigrations. They either need to be updated or removed then make migrationsWatchcase

© 2022 - 2024 — McMap. All rights reserved.