Correct way of getting count on FK - django

What is the correct way of getting the 'contact' count from within my 'Group'?
I was thinking of just creating a new method within 'group' and filter(), but this means hitting the db again which seems bad, right?
class GroupManager(models.Manager):
def for_user(self, user):
return self.get_query_set().filter(user=user,)
class Group(models.Model):
name = models.CharField(max_length=60)
modified = models.DateTimeField(null=True, auto_now=True,)
#FK
user = models.ForeignKey(User, related_name="user")
objects = GroupManager()
def get_absolute_url(self):
return reverse('contacts.views.group', args=[str(self.id)])
class Contact(models.Model):
first_name = models.CharField(max_length=60)
last_name = models.CharField(max_length=60)
#FK
group = models.ForeignKey(Group)

group_object.contact_set.count() should do it. Django creates the relation by adding _set to the end of the foreign key's model name.
Have a look at the docs on related objects for more info.

Related

How do I access instance of a many-to-many field in django __str__ method

I am having two models;
class Cart(models.Model):
book = models.ForeignKey(Book, on_delete=models.CASCADE, related_name="book")
quantity = models.PositiveIntegerField(default=1, null=True, blank=True)
def __str__(self):
return f"{self.quantity} of {self.book.title}"
class Order(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
cart = models.ManyToManyField(Cart)
def __str__(self):
return f"{self.cart.quantity}"
I get:
'ManyRelatedManager' object has no attribute 'quantity'
This usually works for a ForeignKey field; but it seems not to for a ManyToManyField.
Note: I am trying to access quantity that belongs to the cart model from the Order model using ManyToManyField.
Is there any way of doing something like this?
self.cart returns 'ManyRelatedManager'.
you have to write something like this:
self.cart.last().quantity
# OR
self.cart.first().quantity
or get sum of all, with self.cart.all()
sum([_.quantity for _ in self.cart.all()])

I am working with Django, During inserting data into database i caught such error

I'm working with django, during inserting data into tables the error is generates as given below...
Error:
int() argument must be a string, a bytes-like object or a number, not 'Tbl_rule_category', How can we solve such error?
view.py
dataToRuleCtgry = Tbl_rule_category(category=category, created_by="XYZ",created_date=datetime.date.today())
dataToRuleCtgry.save()
dataToRule = Tbl_rule(rule_name=rule_name, closure=closure,category_id=Tbl_rule_category.objects.latest('category_id'), created_by="XYZ",created_date=datetime.date.today(), updated_by="XYZ", updated_date=datetime.date.today(), rule_type=rule_type, fk_tbl_rule_tbl_rule_category_id=Tbl_rule_category.objects.latest('category_id'))
dataToRule.save()
models.py
class Tbl_rule_category(models.Model):
category_id = models.AutoField(primary_key=True)
category = models.CharField(max_length=50)
created_by = models.CharField(max_length=50)
created_date = models.DateField(auto_now_add=True)
def __str__(self):
pass # return self.category, self.created_by
class Tbl_rule(models.Model):
rule_id = models.AutoField(primary_key=True)
rule_name = models.CharField(max_length=50)
closure = models.CharField(max_length=50)
category_id = models.IntegerField()
created_by = models.CharField(max_length=50)
created_date = models.DateField(auto_now_add=True)
updated_by = models.CharField(max_length=50)
updated_date = models.DateField(auto_now=True)
rule_type = models.CharField(max_length=50)
fk_tbl_rule_tbl_rule_category_id = models.ForeignKey(Tbl_rule_category,on_delete=models.CASCADE, related_name='fk_tbl_rule_tbl_rule_category_id_r')
def __str__(self):
return self.rule_name, self.closure, self.created_by, self.updated_by, self.rule_type
The error is occurring because the following is trying to add an object into an integer field: category_id=Tbl_rule_category.objects.latest('category_id')
You could just add: category_id=dataToRuleCtgry.get('category_id') or category_id=dataToRuleCtgry.category_id which will solve the error.
You also don't need to add: created_date=datetime.date.today() because your model defines auto_now=true.
As mentioned you should also amend the def __str__(self): to return a string.
https://docs.djangoproject.com/en/2.0/ref/models/instances/#django.db.models.Model.str
Alternatively
You could just add the object link directly to your foreign key for the category model.fk_tbl_rule_tbl_rule_category_id=dataToRuleCtgry. You would no longer need the integer field category_id.
It would be better practice to use the model field name category_id instead of fk_tbl_rule_tbl_rule_category_id. This would mean deleting category_id and then rename fk_tbl_rule_tbl_rule_category_id to category_id.
In Django, the ORM takes care of the basic database details for you; which means in your code you really don't have to worry about individual row ids for maintaining foreign key relationships.
In fact, Django automatically assigns primary keys to all your objects so you should concentrate on fields that are relevant to your application.
You also don't have to worry about naming fields in the database, again Django will take care of that for you - you should create objects that have fields that are meaningful to users (that includes you as a programmer of the system) and not designed for databases.
Each Django model class represents a object in your system. So you should name the classes as you would name the objects. User and not tbl_user. The best practice is to use singular names. Django already knows how to create plural names, so if you create a model class User, django will automatically display Users wherever it makes sense. You can, of course, customize this behavior.
Here is how you should create your models (we will define __str__ later):
class RuleCategory(models.Model):
name = models.CharField(max_length=50)
created_by = models.CharField(max_length=50)
created_date = models.DateField(auto_now_add=True)
class Rule(models.Model):
name = models.CharField(max_length=50)
closure = models.CharField(max_length=50)
created_by = models.CharField(max_length=50)
created_date = models.DateField(auto_now_add=True)
updated_by = models.CharField(max_length=50)
updated_date = models.DateField(auto_now=True)
rule_type = models.CharField(max_length=50)
category = models.ForeignKey(RuleCategory,on_delete=models.CASCADE)
Django will automatically create any primary or foreign key fields, and any intermediary tables required to manage the relationship between the two models.
Now, to add some records:
new_category = RuleCategory(name='My Category', created_by='XYZ')
new_category.save()
# Another way to set values
new_rule = Rule()
new_rule.name = 'Sample Rule'
new_rule.closure = closure
new_rule.created_by = 'XYZ'
new_rule.updated_by = 'XYZ'
new_rule.rule_type = rule_type
new_rule.category = new_category
new_rule.save()
Note this line new_rule.category = new_category - this is how we link two objects. Django knows that the primary key should go in the table and will take care of that automatically.
The final item is customizing the models by creating your own __str__ method - this should return some meaningful string that is meant for humans.
class RuleCategory(models.Model):
name = models.CharField(max_length=50)
created_by = models.CharField(max_length=50)
created_date = models.DateField(auto_now_add=True)
def __str__(self):
return '{}'.format(self.name)
class Rule(models.Model):
name = models.CharField(max_length=50)
closure = models.CharField(max_length=50)
created_by = models.CharField(max_length=50)
created_date = models.DateField(auto_now_add=True)
updated_by = models.CharField(max_length=50)
updated_date = models.DateField(auto_now=True)
rule_type = models.CharField(max_length=50)
category = models.ForeignKey(RuleCategory,on_delete=models.CASCADE)
def __str__(self):
return '{} for category {}'.format(self.name, self.category)
If you notice something, I just put self.category in the __str__ for the Rule model. This is because we have already defined a __str__ for the RuleCategory model, which just returns the category name; so now when we print our Rule we created, we will get Sample Rule for category My Category as a result.

Django model primary key as a pair

I am trying to make an app where users will login to their profile and can add songs to their favorite list. I am defining a M2M relationship for this.
My question is how to say combination of (song, singer) is unique?
I searched and found that it may be possible through unique_together. Is this the correct way of setting this?
models.py:
from django.contrib.auth.models import User
class Singer(models.Model):
name = models.CharField(max_length=500, unique=True)
def __str__(self):
return self.name
class Song(models.Model):
id = models.AutoField(primary_key=False)
singer = models.ForeignKey(Singer, on_delete=models.CASCADE, related_name='song')
name = models.CharField(max_length=500)
Class Meta:
unique_together = (singer, id)
def __str__(self):
return self.name
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
songs = models.ManyToManyField(Song, related_name='profile')
def __str__(self):
return self.user.username
Please feel free to correct my models.py, if you think the relationship is not correct.
Thanks,
I would use a default primary key (auto field), and use the meta class property, unique_together
class Song(models.Model):
singer = models.ForeignKey(Singer, on_delete=models.CASCADE, related_name='song')
name = models.CharField(max_length=500)
class Meta:
unique_together = (("singer", "name"),)
It would act as a "surrogate" primary key column.
You don't specify id in your model song. I would also recommend to use slug field of django and specify unique on the same. Just in case you have two singers with the same name. Then the second or more you put like abc, abc-1, abc-2, in which case you don't have to change the name and unique_together clause works just fine.
class Song(models.Model):
singer = models.ForeignKey(Singer, on_delete=models.CASCADE, related_name='song')
name = models.CharField(max_length=500)
class Meta:
unique_together = (("singer", "name"),)
def __str__(self):
return self.name

Order a ManyToMany relationship, Django

With these models:
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
order_index = models.IntegerField(unique=True)
class Meta:
ordering = ['order_index']
I've been trying to get the following code to work:
group = Group.objects.get(id=1)
group.members.all() # I want the resulting membership objects to ordered by order_index as default.
Is this possible? The answers to similar questions require you to write group.members.all().order_by(...). I am trying to have it so the results are ordered by order_index by default.
This is how you get the user, and then the members associated with that user ordering them by date_joined. Not sure how you would do it automatically because the ordering is usually by the pk value which I'm assuming would correspond with the date joined because the higher the pk the later joined.
user = Person.objects.get(pk=request.user.id)
get_members = user.group.members.all().order_by('date_joined')

Model with Foreign keys not behaving as expected - Django

I have a model with two foreign keys to create many to many relationship - I am not using many to many field in Django
class Story(models.Model):
title = models.CharField(max_length=200)
pub_date = models.DateTimeField('date published')
def __unicode__(self):
return self.title
class Category(models.Model):
categoryText = models.CharField(max_length=50)
parentCat = models.ForeignKey('self',null=True,blank=True)
def __unicode__(self):
return self.categoryText
class StoryCat(models.Model):
story = models.ForeignKey(Poll,null=True,blank=True)
category = models.ForeignKey(Category,null=True,blank=True)
def __unicode__(self):
return self.story
I would like to query for a category like 'short', and retrieve all the unique keys to all stories returned.
>>>c=Category(categoryText='short')
>>>s=StoryCat(category=c)
when I try this I get errors "AttributeError: 'NoneType' object has no attribute 'title'. How can I do this?
I would like to query for a category like 'short', and retrieve all the unique keys to all stories returned.
c=Category.objects.get(categoryText='short')
story_ids = StoryCat.objects.filter(category=c).values_list('story')
And about your models:
Category name should probably be unique. And declare your many-to-many relation.
class Category(models.Model):
categoryText = models.CharField(max_length=50, unique=True)
stories = models.ManyToManyField(Story, through='StoryCat')
...
It makes no sense for intermediary table FK fields to be nullable. Also I assume the same story should not be added to the same category twice, so set a unique constraint.
class StoryCat(models.Model):
story = models.ForeignKey(Poll)
category = models.ForeignKey(Category)
class Meta:
unique_together = ('story', 'category')
The lines you're executing in the interpreter are not queries - they are instantiating new objects (but not saving them).
Presumably you meant this:
>>>c=Category.objects.get(categoryText='short')
>>>s=StoryCat.objects.get(category=c)