0

I am using Django sites framework (Django 2.1) to split an app into multiple sites. All of my models except the User model are site-specific. Here is my Post model:

post.py

class Post(models.Model):
    parent = models.ForeignKey(
        'self',
        on_delete=models.CASCADE,
        related_name='children',
        related_query_name='child',
        blank=True,
        null=True,
    )
    title = models.CharField(
        max_length=255,
        blank=True,
    )
    body_raw = models.TextField()
    body_html = models.TextField(blank=True)
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        on_delete=models.CASCADE,
    )
    site = models.ForeignKey(Site, on_delete=models.CASCADE)
    on_site = CurrentSiteManager()

I have no problem separating posts out by site. When I want to get the posts, I call:

posts = Post.on_site.filter(...)

I have a separate model called UserProfile. It is a many-to-one profile where there is a unique profile created for each user-site combination (similar to profile implementation at SE). The profile has a reputation attribute that I want to access when I get any post. This reputation attribute should be different for each site (like how on SE you have different rep on each site you are a member of).

user_profile.py

class UserProfile(models.Model):
    user = models.ForeignKey(get_user_model(), on_delete=models.CASCADE)
    reputation = models.PositiveIntegerField(default=1)
    site = models.ForeignKey(Site, on_delete=models.CASCADE)
    on_site = CurrentSiteManager()

How do I access the user's username (on the User model) as well as the user's reputation (on the UserProfile model) when I get Posts from a query?

I'd like to do something like:

Post.on_site.filter(...)
    .select_related('user__userprofile')
    .filter_related(user__userprofile.site == get_current_site())

How do I filter a Many-To-One related model?

Jacob
  • 1,560
  • 4
  • 19
  • 41

1 Answers1

1

Better to make UserProfile -> User relationship to be OnetoOne,
because Django doesn't know which of many profiles to show

(but you also need to define related_name)

models.OneToOneField(get_user_model(), related_name='userprofile_rev')

Then you will be able to do this

qs = Post.on_site.filer().select_related('user', 'user__userprofile_rev')
for post in qs:
    print(post.user.username, post.user.userprofile_rev.reputation)

If you don't want to change your DB structure you can do like this
(but you need to specify which profile to return)

qs = Post.on_site.filer().select_related('user').prefetch_related('user__userprofile_set')
for post in qs:
    print(post.user.username, post.user.userprofile_set[0].reputation)
Artem Bernatskyi
  • 4,185
  • 2
  • 26
  • 35
  • But I want different reputation values on each site... Would there be a better way to do this using your method while still keeping rep separate for each site? – Jacob Sep 09 '18 at 02:40
  • @Jacob , then you need to filter that related set somehow `post.user.userprofile_set.filter(...).reputation` – Artem Bernatskyi Sep 09 '18 at 02:46