Django: follow relations backwards - python

Hey, I have models like this:
class Galleries(models.Model):
creation_date = models.DateTimeField()
name = models.CharField(max_length=255, unique=True)
gallery_type = models.ForeignKey(Categories)
class Categories(models.Model):
handle = models.CharField(max_length=255, unique=True)
class Values(models.Model):
category = models.ForeignKey(Categories)
language = models.CharField(max_length=7)
category_name = models.CharField(max_length=50)
And now, I just want to reach the values of categories by starting from Galleries. For example: galleries = Galleries.objects.get(id=1). And now I want to reach somehow the values by using this "galleries" object... To get values with specific language would be much more better... I miss skills in Django ORM, so if you can, please point me to some docs or give some code example. Thanks!

galleries = Galleries.objects.get(id=1)
values = galleries.gallery_type.values_set.filter(language='language')
Interestingly, you used the exact wording that the docs use to refer to the related field lookups. I always found the definition strange to the gut, maybe because they put it in quotes.
FOLLOWING RELATIONSHIPS "BACKWARD"
http://docs.djangoproject.com/en/1.2/topics/db/queries/#following-relationships-backward

You may want to use the select_related method of objects so you reduce the number of queries you are making. select_related
gallery = Galleries.objects.select_related().get(id=1)
You can set a related name for the Values model in the category fk:
class Values(models.Model):
category = models.ForeignKey(Categories, related_name="categories")
language = models.CharField(max_length=7)
category_name = models.CharField(max_length=50)
now you can get your list of values for a specific language by doing
values = gallery.gallery_type.categories.filter(language="language")

Related

django distinct in template by filter

I need to display unique values in template. I know distinct will work in query. But table in one row Many to Many field I don't know how to implement for that. If any possibilities are there to use it in template
class DoctorFeedback(models.Model):
visited_for = models.CharField(max_length=200)
tag = models.ManyToManyField(Tags)
class Tags(models.Model):
tag_title = models.CharField(max_length=50)
You can add unique=True to Tags, so there will never be any duplicates:
class Tags(models.Model):
tag_title = models.CharField(max_length=50, unique=True)

Using annotate or extra to add field of foreignkey to queryset ? (equivalent of SQL "AS" ?)

I have merged two querysets (qs1 and qs2) which individually work fine, as follows:
qlist = [qs1, qs2]
results = list(chain(qs1, qs2))
So far, so good - the above works. But now I'm trying to order the results using the following:
qlist = [qs1, qs2]
results = sorted(chain(qs1, qs2), key=attrgetter('monthly_fee'))
The problem is that the second queryset (qs2) refers to the monthly_fee through a ForeignKey; whereas qs1 has 'monthly_fee' available. Here is qs2:
qs2 = Offer.objects.select_related('subscription')
qs2 = qs2.order_by(subscription__monthly_fee)
And the simplified models:
class Subscription(models.Model):
monthly_fee = models.IntegerField(null=False, blank=True, default=0)
name = models.CharField(max_length=120, null=True, blank=True)
class Offer(models.Model):
promotion_name = models.CharField(max_length=120, null=True, blank=True)
subscription = models.ForeignKey(Subscription)
discount = models.IntegerField(null=False, blank=True, default=0)
I've tried using .annotate() and .extra() to rename the subscription__monthly_fee in the query qs2 as follows:
qs2 = Offer.objects.select_related('subscription').annotate(monthly_fee=subscription__monthly_fee)
But then get the error
global name 'subscription__monthly_fee' is not defined
I am at the point of just hacking this by over-riding the .save() methods of my models to manually add the monthly_fee to each Offer instance whenever an object is created. But just wanted to check whether there isn't a better way ?
Thank you,
Michael
I've used an F expression to achieve this sort of renaming before. Try this:
from django.db.models import F
qs2 = Offer.objects.select_related('subscription').annotate(monthly_fee=F('subscription__monthly_fee'))
OK, I found a way to do this.
qs2 = Offer.objects.select_related('subscription').extra(select={'monthly_fee':'mobile_subscription.monthly_fee'})
where 'mobile' is the name of the Django app. I didn't realize that .extra DOES allow you to follow foreign keys but that you actually have to specify the actual database table and use SQL dot notation.
Is the above the actual correct way we are supposed to do it ? (i.e. dropping in raw SQL table names/fields)
I had been trying to use Django syntax such as .extra(select={'monthly_fee':'subscription__monthly_fee'}) which doesn't work!

Django count foreign key through relation

I am building a simple forum application and I need some help with counting foreign key objects via through relation.
Let's say my models look like this:
class Forum(models.Model):
title = models.CharField(max_length=255)
description = models.TextField()
slug = models.SlugField(unique=True, blank=True)
class Thread(models.Model):
title = models.CharField(max_length=255)
forum = models.ForeignKey(to=Forum, related_name='threads')
slug = models.SlugField(unique=True, blank=True)
class Post(models.Model):
body = models.TextField()
author = models.ForeignKey(User)
thread = models.ForeignKey(to=Thread,related_name='posts')
Now we create forum object.
forum = Forum.objects.get(slug=forum)
We can count the number of threads inside forum like this: forum.threads.count()
My question is, how can i count all the posts in a forum?
I've tried something like all_posts = forum.thredas.posts.count() but as expected it didn't work.
Thanks!
Generally in Django it is a good principle that when you want to do something with a model, you should start your query from that model. So, since you need to count posts, you should start with the Post model.
From there you can use the double-underscore syntax to filter to the particular Forum you want.
forum_posts = Post.objects.filter(thread__forum=my_forum)
forum_post_count = forum_posts.count()

Django queryset and GROUP BY

I'm struggling with django querysets and GROUP BY queries, I know there are plenty of similar questions, but I really don't get it:/
I would like to be able to create a request similar to this one (SQLite):
SELECT MAX(fb_game_score.value), fb_game_fbuser.first_name, fb_game_fbuser.last_name
FROM fb_game_score
JOIN fb_game_game ON (fb_game_score.game_id = fb_game_game.id)
JOIN fb_game_fbuser ON (fb_game_game.user_id = fb_game_fbuser.id)
GROUP BY fb_game_fbuser.fb_user_id;
The query is quite simple, it lists the users scores by showing only the best score for each players.
For clarification here's the model classes:
class FBUser(AbstractUser):
fb_user_id = models.CharField(max_length=100, null=True)
oauth_token = models.CharField(max_length=1024, null=True)
expires = models.IntegerField(null=True)
highest_score = models.IntegerField(null=True)
class Game(models.Model):
identifier = models.CharField(max_length=100, db_index=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='games')
class Score(models.Model):
game = models.ForeignKey(Game, related_name='scores')
value = models.IntegerField()
date = models.DateTimeField(auto_now=True)
timestamp = models.FloatField(default=0)
inter = models.BooleanField(default=False)
There's no high-level group_by in the queryset. It's used in calls to aggregate and annotate but it is not available to you.
There's a low-level API which is not documented at all. You can get an internal query description:
queryset = ... #whatever query you'd want to group by
query = queryset.query
and then you can alter the group_by member -which is a list- by adding a field which you'd want to group by:
query.group_by.append('a_field')
But:
you have to seriously know what you're doing.
there's no guarantee of stability of this API.
The current alternative for this is falling back to a raw (django.db.connection.* methods) SQL query.
Edit: I just saw this 3rd-party application which could help you with reports. I don't know if you can use in-code reports, or you have to limit yourself to in-view reports (i.e.: don't know if you can process reports in code or just have them as final results).

django taggit prevent overlapping tags across different models

I have two different models.
class MessageArchive(models.Model):
from_user = models.CharField(null=True, blank=True, max_length=300)
archived_time = models.DateTimeField(auto_now_add=True)
label = models.ForeignKey(MessageLabel, null=True, blank=True)
archived_by = models.ForeignKey(OrgStaff)
tags = TaggableManager()
Now say, I have defined spam, todo, urgent tags for messages.
and then I have another model:
class PersonArchive(models.Model):
from_user = models.CharField(null=True, blank=True, max_length=300)
archived_time = models.DateTimeField(auto_now_add=True)
label = models.ForeignKey(MessageLabel, null=True, blank=True)
archived_by = models.ForeignKey(OrgStaff)
tags = TaggableManager()
I define awesome, legend, rockstar for the model person. There might few more be defined.
As is quite clear, I do not want the tags for person and message to overlap.
How should I achieve that? Thanks!
You can utilize the limit_choices_to feature on ForeignKeyFields and ManyToManyFields. Your models.py file might look like this:
class PersonArchive(models.Model):
tags_field = models.ManyToManyField(Tag, related_name="people_archives", limit_choices_to={'message_archives__isnull': True})
class MessageArchive(models.Model):
tags_field = models.ManyToManyField(Tag, related_name="message_archives", limit_choices_to={'people_archives__isnull': True})
You your case, as far as I understand, you want different base families of tag for the two different models.
Take in account I'm not an expert on taggit, so the solution I'm proposing can be a bit overcomplitated, but is the first that jump me in mind by looking at the source code.
You can achieve that by extending the TaggableRel class used by TaggableManager and add a condition to the limit_choices_to parameter:
Extend TaggableRel
class CustomTaggableRel(TaggableRel):
def __init__(self, field, model_name):
super(TaggableRel, self ).__init__(field)
self.limit_choices_to = {'content_type': model_name}
Than you extend TaggableManager in the following way:
class CustomTaggableManager(TaggableManager):
def __init__(self, model_name=None, verbose_name=_("Tags"),
help_text=_("A comma-separated list of tags."), through=None, blank=False):
super(TaggableManager, self ).__init__(verbose_name, help_text, through, blank)
self.rel = CustomTaggableRel(self, model_name)
Than you your models:
class PersonArchive(models.Model):
.
.
.
tags = CustomTaggableManager(model_name="PersonArchive")
This should do the trick, didn't try the solution and I write it down very fast, but this could drive you on the right path.
Just dealt with this myself. I decided to let my tags intermingle because I found a way to only filter tags for specific models. This will only filter tags for modelname. You can expand the filter as desired.
Tag.objects.filter(taggit_taggeditem_items__content_type__model='modelname')

Categories

Resources