Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Usage - Making tags on a per user basis #809

Open
CarloCogni opened this issue Jul 1, 2022 · 6 comments
Open

Usage - Making tags on a per user basis #809

CarloCogni opened this issue Jul 1, 2022 · 6 comments

Comments

@CarloCogni
Copy link

Hi,

in my project there is a user case where I need to make the tags associated with the user. This way every user can create their own set of tags, also when I present a page with all tags I could to something like Tag.objects.filter(user=request.user)

I've asked a question on StackOverflow, but I'm thinking I could be useful to ask you directly.
https://stackoverflow.com/questions/72823187/django-how-to-make-tags-on-a-per-user-basis.

Let me know if I can provide with more informations.

Thank you very much.

Cheers

Carlo

@chambersh1129
Copy link
Member

Try using the TaggedItem instead of Tag for your query, it has a class method that returns a queryset of Tags for the object provided. Something like this should do it:

from taggit.models import TaggedItem

tags = TaggedItem.tags_for(request.user)

@rtpg
Copy link
Contributor

rtpg commented Nov 14, 2022

@CarloCogni Basically you're most of the way there, just call MyTag.objects.filter(user=request.user) as a default queryset in various places. This does mean that when you set or add tags you'll need to pass in the right tag_kwargs.

from the django-taggit test code:

        apple = OfficialFood.objects.create(name="apple")

        # let's add two official tags
        apple.tags.add("foo", "bar", tag_kwargs={"official": True})

        # and two unofficial ones
        apple.tags.add("baz", "wow", tag_kwargs={"official": False})

here our custom tag model has an extra official boolean, in your case it's user. You'll just need to do stuff like this (or outright create custom models. You don't have to use our helpers!)

@rtpg
Copy link
Contributor

rtpg commented Nov 14, 2022

Would be good to add this to the FAQ, as multitenant keys is a super common thing that people need to deal with.

@ethagnawl
Copy link

Apologies for potentially hijacking this thread but I'm confused about the example referenced above and how it applies to OP's question. If two users attempted to use the same tags, wouldn't that result in a uniqueness validation error? Is this the sort of situation you were considering when suggesting the end user create their own models, @rtpg?

For example:

user_1_apple.tags.add("foo", tag_kwargs={"user_id": 1})
user_2_apple.tags.add("foo", tag_kwargs={"user_id": 2})

If I modify the taggit test case referenced above in accordance with my example, the test fails with:

django.db.utils.IntegrityError: UNIQUE constraint failed: tests_officialtag.name

If a custom model was used and the uniqueness constraint was a compound one across name and user_id, would that just work with the surrounding taggit machinery?

@rtpg
Copy link
Contributor

rtpg commented Jun 13, 2023

@ethagnawl thanks for the question, it does feel like we should document a full flow for this

I think there are multiple ways to do this, here is a way that I handled this a while back:

from taggit.models import TaggableManager, GenericTaggedItemBase


class MyTag(models.Model):
    user = models.ForeignKey(...)
    name = models.CharField(....)

class MyTaggedItem(GenericTaggedItemBase):
    tag = models.ForeignKey(MyTag, ...)


class MyResource(models.Model):
    # this allows me to put tags on the resource
    tags = TaggableManager(blank=True, through=MyTaggedItem)

The core point here is that I set up a tagged item model, but provide my own tag class entirely (thus being able to set my unique constraints myself instead of using the ones set in taggit.models.Tag). Then MyResource can use the tags.

There might be a way to subclass Tag itself for MyTag in this (you could replace name yourself? Or otherwise changeup the uniqueness constraint), but this is all very much a "make sure to write tests" thing.

@ethagnawl
Copy link

Thanks for following up, @rtpg!

That's pretty close to what I started sketching out yesterday after asking my question above. So far, so good but you're spot on about needing to lean on tests. I'm happy to potentially help with documentation once I'm satisfied with my solution.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants