Django: way of getting objects? - django

I have following structure of models
For the model of 'Campaigns' I'm going to write down functions that would be called in a template: ads_count
My solution was:
class Campaigns(AlphaModel):
Project = models.ForeignKey('Projects', on_delete=models.CASCADE, related_name='projects')
......
def ads_count(self):
c = 0
gr = AdGroups.objects.filter(Campaign=self).all()
for g in gr:
c += g.ads_count()
return c
......
class AdGroups(AlphaModel):
Campaign = models.ForeignKey('Campaigns', on_delete=models.CASCADE, related_name='campaigns')
server_id = models.PositiveIntegerField()
......
def ads_count(self):
return Ads.objects.filter(AdGroup=self).count()
......
class Ads(AlphaModel):
AdGroup = models.ForeignKey('AdGroups', on_delete=models.CASCADE, related_name='adgroups_a')
server_id = models.PositiveIntegerField()
......
However this was throws an error "Cannot resolve keyword into field"
Is there any simple and direct way of counting objects of 'ads' that belongs to particular object of 'campaigns' ?
Thanks in advance!

First of all, it is general convention to use CamelCase only for class definitions, and lower case in all your field names. This makes it much easier to follow the code and distinguish between classes and objects. I would rewrite your models as follows:
class Campaigns(AlphaModel):
project = models.ForeignKey('Projects', on_delete=models.CASCADE, related_name='projects')
class AdGroups(AlphaModel):
campaign = models.ForeignKey('Campaigns', on_delete=models.CASCADE, related_name='campaigns')
server_id = models.PositiveIntegerField()
class Ads(AlphaModel):
ad_group = models.ForeignKey('AdGroups', on_delete=models.CASCADE, related_name='adgroups_a')
server_id = models.PositiveIntegerField()
Now, to get the count of Ads associated with a Campaign, you can do this query:
class Campaigns(AlphaModel):
...
def ads_count(self):
return Ads.objects.filter(ad_group__campaign=self).count(distinct=True)
(using your existing field names this would be):
Ads.objects.filter(AdGroup__Campaign=self).count(distinct=True)

Related

Exporting and Importing a foreign key field that has a many-to-many relationship in Django

I've got a complicated relationship between my Django models and I'm trying to get django-import-export to play nicely.
class Person(models.Model):
name = models.CharField(max_length=64)
class Team(models.Model):
rep = models.ManyToManyField(Person, related_name="rep")
def get_reps(self):
return "/".join(sorted([p.name for p in self.reps.all()]))
class Account(models.Model):
tid = models.IntegerField("Territory ID", primary_key=True)
name = models.CharField("Territory Name", max_length=64)
sales_team = models.ForeignKey(Team, related_name="sales_team")
I'm trying to export (and hopefully later import) the territories with the names of the reps as rendered by the get_reps method.
class TerritoryResource(resources.ModelResource):
tid = fields.Field(attribute='tid', column_name="Territory ID")
name = fields.Field(attribute='name', column_name="Territory Name")
sales_team = fields.Field(
column_name="Sales Team",
widget=widgets.ForeignKeyWidget(Team, "get_reps")
)
The export is giving me a blank field. If I don't use the widget I get the Team ID as I'd expect.
Is it possible to get my custom name in the export?
I didn't include the standard __str__ methods in the sample code, because I didn't think they were important, but I did have in my Team class definition:
def __str__(self):
return self.get_reps()
This means, had I read the documentation with a little more creativity, I would have figured out how to do this. It's deceptively simple:
class TerritoryResource(resources.ModelResource):
...
def dehydrate_sales_team(self, territory):
return str(territory.sales_team)
I could also use return territory.sales_team.get_reps() to get the same results.

Django query based on another query results

I have 4 models in my simplified design
class modelA(models.Model):
name = models.CharField()
class modelsUser(model.Model):
username = models.CharField()
class bridge(models.Model):
user = models.ForeignKey(modelUser, on_delete=models.CASCADE, related_name='bridges')
modelA = models.ForeignKey(modelA, on_delete=models.CASCADE, related_name='bridges')
class subModelA(models.Model):
modelA = models.ForeignKey(modelA, on_delete=models.CASCADE, related_name='subModelAs')
value = models.IntegerField()
class subModelB(models.Model):
modelA = models.ForeignKey(modelA, on_delete=models.CASCADE, related_name='subModelBs')
text = models.TextField()
What I am trying to to is to get all subModelBs and subModelAs that are for modelAs for which given modelUser have bridge.
I've started with this:
user = modelUser.objects.get(pk=1)
bridges = user.bridges.all()
What I've been thinking is something like this:
subModelBs = subModelB.objects.filter(modelA__in=bridges__modelA)
but unfortunately it doesn't work because of error that __modelA is not defined.
Is there any proper way to do this?
Find first the modelAs and then do two other queries:
modelAs = bridge.objects.filter(user__pk=1).values_list('modelA', flat=True)
subModelAs = subModelA.object.filter(modelA__in=modelAs)
subModelBs = subModelB.object.filter(modelA__in=modelAs)
A good question first of all!
Tried reproducing on my system, the following worked for me:
user = modelUser.objects.get(pk=1)
bridges = user.bridges.all()
subModelAs = subModelA.objects.filter(
modelA_id__in=[x.modelA_id for x in list(bridges)]
)
And similarly for subModelBs. Hope this helps you well.

Generic foreign key in Django showing error must be instance of Content Type

I have a following abstract Class
class Manufacturer(models.Model):
company=models.CharField(max_length=255)
class Meta:
abstract = True
Now 2 classes inherit from above:-
class Car(Manufacturer):
name = models.CharField(max_length=128)
class Bike(Manufacturer):
name = models.CharField(max_length=128)
Now I want to link them with features, so I create following classes:-
class Feature(models.Model):
name= models.CharField(max_length=255)
limit=models.Q(model = 'car') | models.Q(model = 'bike')
features = models.ManyToManyField(ContentType, through='Mapping',limit_choices_to=limit)
class Mapping(models.Model):
category=models.ForeignKey(Category, null=True, blank=True)
limit=models.Q(model = 'car') | models.Q(model = 'bike')
content = models.ForeignKey(ContentType, on_delete=models.CASCADE,limit_choices_to=limit,default='')
object_id = models.PositiveIntegerField(default=1)
contentObject = GenericForeignKey('content', 'object_id')
class Meta:
unique_together = (('category', 'content','object_id'),)
db_table = 'wl_categorycars'
But When i try creating instances in shell commands I get an error while creating mapping instance
"Mapping.content" must be a "ContentType" instance.
car1=Car(company="ducati",name="newcar")
bike1=Bike(company="bike",name="newbike")
cat1=Category(name="speed")
mapping(category=cat1, content=car1) # ---> i get error at this point
How do I proceed with this?
You need to create your object with:
Mapping(
category=cat1,
content=ContentType.objects.get_for_model(car1),
object_id=car.id
)
By the way, I would have named the field content_type instead of content to avoid ambuiguity. See the official documentation for more information.
You are supposed to use contentObject parameter for populating model objects as GenericForeignKey instead of content.
Something like this should work:
Mapping(category=cat1, contentObject=car1)

Query in django models

Reward models.py
class Reward(CommonInfo):
approved = models.BooleanField(default=False)
manager = models.ForeignKey(OrganisationUser, related_name='rewards_given') #todo add contraint so that manager should be manager of this role
approver = models.ForeignKey(OrganisationUser, null=True, related_name='approved_rewards', blank=True)# todo same as above but approver
number_of_gems = models.PositiveIntegerField(null=True, db_column='number_of_gems', blank=True)
tag = models.ForeignKey(Tag,related_name='rewards')
role_history = models.ForeignKey(RoleHistory, related_name='rewards')
certificate = models.OneToOneField(Certificate,related_name='reward')
and certificate models.py :
class Certificate(models.Model):
comments = models.TextField(blank=True, default='')
generic_certificate = models.ForeignKey(GenericCertificate, related_name='certificates_awarded')
tag = models.ForeignKey('Tag', related_name='certificates_awarded', null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
history_timestamp = models.DateTimeField(auto_now=True)
#template = models.FileField(upload_to='certificate/rewarded_templates', null=True, blank=True)#this will be path of certificate generated for this particular employee
rewardee = models.ForeignKey(OrganisationUser, related_name='certificates_rewarded')
#there will be location in server for certificates and it will be auto generated.
I have query to take out rewardee names from rewards models :
a= Reward.objects.filter(approved=True)
print a
Its printing : [<Reward: reciever-nirmal_4, tag-123>, <Reward: reciever-nirmal_1, tag-SDF34>]
I want to fetch nirmal_4 and nirmal_1 using this query . These are rewardee names.
How to do this?
Its printing [<Reward: reciever-nirmal_4, tag-123>, <Reward: reciever-nirmal_1, tag-SDF34>] because those are Reward objects. And in fact, it is fetching exactly those two objects which you want.
Now, if you want to print rewardee names, then you can loop over the objects and print rewardee names as:
a= Reward.objects.filter(approved=True)
for each in a:
print each.approver
Here, approver is still an object of OrganisationUser. So, if this object has a name then you can do print each.approver.name
Another option is, in your models.py, define a unicode function to the model as:
def __unicode__(self):
return self.approver.name
This function is used to set the name of object for recognizing it. So, you can set the name of reward object with its approver name.

How to make a query on related_name field?

I have to models connected by a ForeignKey
class User(AbstractUser):
...
and
class PrivateMessage(models.Model):
user_from = models.ForeignKey(
User,
verbose_name=u'From',
related_name='sent_messages',
)
user_to = models.ForeignKey(
User,
verbose_name=u'To',
related_name='received_messages',
)
Is there any way to get all the addresses for a particular user. For example, if
u = User.objects.get(id=1)
messages = PrivateMessage.objects.filter(user_from=u)
for m in messages:
users.add(m.user_to)
How to obtain a list of users that appear in user_to for these messages using only Django ORM methods?
I think a better idea would be to define ManyToManyField on the User model:
class User(AbstractUser):
#...
receivers = models.ManyToManyField('self', through='Message',
symmetrical=False, related_name="senders")
class Message(models.Model):
user_from = models.ForeignKey(MyUser, related_name='messages_from')
user_to = models.ForeignKey(MyUser, related_name='messages_to')
message = models.CharField(max_length=100, default="")
#...
Then to retrieve users list on the other end you simply do:
User.objects.get(id=1).receivers.all() # who I sent the message to
User.objects.get(id=1).senders.all() # who sent me a message
This way you have a nice clear API.
Finally, I ended up writing three queries:
users_from = set(PrivateMessage.objects.filter(
user_to=self.request.user,
).values_list(
'user_from__pk',
flat=True,
))
users_to = set(PrivateMessage.objects.filter(
user_from=self.request.user,
).values_list(
'user_to__pk',
flat=True,
))
interlocutors = User.objects.filter(pk__in=users_from.union(users_to))
I saw this docs
Maybe you can try:
u = User.objects.get(id=1)
users = User.objects.filter(received_messages__user_from=u).distinct()
related_name field makes our queries especially the ones using foreign key (on to many relation) easier, shorter and cleaner.
Let say we have 2 models classes Library and Book.
class Library(Models.model):
name = models.CharField(max_length = 100)
`class Book(Models.model):
title = models.CharField(max_length = 100)
library = models.ForeignKey(Library,
on_delete = models.CASCADE,
related_name = 'books')`
Here we have a one to many relation from Library to Book using foriegn key.
And in my django shell. I can create a new Library and a book related to that library in the following manner.
`from <app_name>.models import *`
`library = Library.objects.create(name = 'Big Library')`
`Book.objects.create(title = 'Awesome book', library = library`
Now I can query the book of the library using related name of model Book class:
`library.books.all()`
rather than using the starting the query from Book model as:
Book.objects.filter(library = library)