Model with Foreign keys not behaving as expected - Django - 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)

Related

Syntax to reverse-query a cached queryset

I have the following 3 models related by Foreign Key as following:
class Seller(models.Model):
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Genre(models.Model):
seller= models.ForeignKey(Seller, related_name="genre", on_delete=models.CASCADE)
name = models.CharField(max_length=20)
def __str__(self):
return self.name
class Book(models.Model):
genre= models.ForeignKey(Genre, related_name="book", on_delete=models.CASCADE)
name = models.CharField(max_length=20)
def __str__(self):
return self.name
And I want to retrieve the whole 3 tables in one database query, by querying the Seller objects, as following:
sellers = Seller.objects.select_related('genre', 'book').all().values('name')
seller_df = pd.DataFrame(list(sellers))
What is the syntax to filter for books carried by a particular seller, without hitting the database again (by utilizing either the Seller queryset or the pandas seller_df)
seller1 = seller_df ['name'].iloc[0]
seller1_books = Book.objects.filter(...)
seller_last = seller_df ['name'].iloc[-1]
seller_last_books = Book.objects.filter(...)
I dont know so mach about caching but I know something that you like:
We use select_related when the object is single like onetoone or fk.
.
for many to many or reverse fk like your example use prefetch_related

Many to Many in two directions

I have a friend that requested something that I was thinking would be simple and quick. It never turns out that way. Quick disclaimer, model design is a krux of mine. I often spend too long perfecting it only to have to rework it several times. Anyway, here is the current state of my model. For everything, it works, except when creating 'raids'.
from django.db import models
# Create your models here.
class PlayerRole(models.Model):
"""
PlayerRole Model
"""
role = models.CharField(max_length=20)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.role
# this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
class Meta:
unique_together = ['role']
class PlayerClass(models.Model):
"""
PlayerClass Model
"""
name = models.CharField(max_length=100)
color = models.CharField(max_length=6)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
# this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
class Meta:
unique_together = ['name']
class Player(models.Model):
"""
Player Model
"""
name = models.CharField(max_length=100)
playerclass = models.ForeignKey(PlayerClass, on_delete=models.CASCADE, blank=True, null=True)
playerrole = models.ForeignKey(PlayerRole, on_delete=models.CASCADE, blank=True, null=True)
value = models.IntegerField(default=0)
reliability = models.IntegerField(default=0)
last_drop = models.DateField(auto_now=False, blank=True, null=True)
last_raid_attended = models.DateField(auto_now=False, blank=True, null=True)
last_boss_attended = models.DateField(auto_now=False, blank=True, null=True)
drop_received = models.BooleanField(default=False)
note = models.TextField(null=True, blank=True)
core_raider = models.BooleanField(default=False)
enabled = models.BooleanField(default=True)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
# this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
class Meta:
unique_together = ['name']
class WoWInstance(models.Model):
"""
Instance Model
"""
name = models.CharField(max_length=100)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
# this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
class Meta:
unique_together = ['name']
class Boss(models.Model):
"""
Boss Model
"""
name = models.CharField(max_length=100)
instance = models.ForeignKey(WoWInstance, on_delete=models.CASCADE)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return self.name
# this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
class Meta:
unique_together = ['name']
class Raid(models.Model):
"""
Raid Model
"""
date = models.DateTimeField(auto_now_add=True)
boss = models.ForeignKey(Boss, on_delete=models.CASCADE, null=True, blank=True)
success = models.BooleanField()
attendees = models.ManyToManyField(Player)
# this function will be invoked when this model object is foreign key of other model(for example Employee model.).
def __str__(self):
return str(self.date) + ' + ' + self.boss.name
# this is a inner class which is used to define unique index columns. You can specify multiple columns in a list or tuple.
class Meta:
unique_together = ['boss']
The idea here is simply to track attendance on each attempt at fighting something in a game. In the most general and simple sense - Many Raids can contain many of the same Bosses. Many Raids can contain many of the same Players.
Now, I thought that because Django automatically creates ID fields, this method wouldn't be an issue. But, it seems not to be the case. If I create a raid with the same Boss more than once, I get...
Raid with this Boss already exists.
What am I missing here? Since all Raid and Boss are unique, shouldn't the two never overlap?
If I create a raid with the same Boss more than once, I get...
Raid with this Boss already exists.
Well that is due to the unique_together constraint:
class Raid(models.Model):
# ...
class Meta:
unique_together = ['boss']
If you write unique_together, it means you enforce a uniqness constraint on a combination of fields. But since you here have mentioned only one field ('boss'), you thus add a unique=True constraint on that specific field.
It thus means that no two Raids can exist with the same boss field. You probably want to remove that.
Since all Raid and Boss are unique, shouldn't the two never overlap?
The Raids and Bosses are already unique. An object does not belong to two models at once (unless one is the subclass of another, but let us ignore that case). You even made the name unique as well (although you better do that by adding a unique=True parameter to the name fields). So there is no need at all to make the 'boss' unique here, since you clearly do not want that.

In Django REST framework, how do you access a field in a table referenced by a foreign key

I have the following models in Django:
class campaign(models.Model):
start_date = models.DateField('Start Date')
end_date = models.DateField('End Date')
description = models.CharField(max_length=500)
name = models.CharField(max_length=200)
active_start_time = models.TimeField()
active_end_time = models.TimeField()
last_updated = models.DateTimeField('Date updated',auto_now=True)
active = models.BooleanField(default=True)
client_id = models.ForeignKey('client',on_delete=models.PROTECT)
def __unicode__(self):
return u'%d | %s | %s' % (self.id,self.name, self.description)
class campaign_product(models.Model):
product_id = models.ForeignKey('product',on_delete=models.PROTECT)
last_updated = models.DateTimeField('Date updated',auto_now=True)
campaign_id = models.ForeignKey('campaign',on_delete=models.PROTECT)
class product(models.Model):
name = models.CharField(max_length=200)
description = models.CharField(max_length=500)
sku = models.CharField(max_length=200,blank=True,null=True)
retail_price = models.DecimalField(decimal_places=2,max_digits=11)
discount_price = ((1,'Yes'),(0,'No'))
discounted_price = models.DecimalField(decimal_places=2,max_digits=11,blank=True,null=True)
category_id = models.ForeignKey('category',on_delete=models.PROTECT)
last_updated = models.DateTimeField('Date updated',auto_now=True)
active = models.BooleanField(default=True)
def __unicode__(self):
return u'%d | %s' % (self.id, self.name)
I also have the following serializer:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id')
And the following view set behavior in the urls.py file:
class campaignProductViewSet(viewsets.ModelViewSet):
queryset = campaign_product.objects.filter(campaign_id__start_date__lte=datetime.now(),campaign_id__end_date__gte=datetime.now(),campaign_id__active__exact=True)
serializer_class = campaignProductSerializer
My problem is I need to include the name field from the products model in my query results when for instance a request is made on http://127.0.0.1:8000/campaign_product/1/. Currenly this request returns only the product_id and the campaign_id. I tried making the serializer as follows:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id', 'product.name')
But then the service returns the following error:
Field name `product.name` is not valid for model `campaign_product`.
I event tried using product__name with and without quotes. Without quotes it tells me that there is no such variable, and with quotes it gives the is not valid for model error similar to the above. Heeelp! Getting this extra field is proving to be a pain :-(
What you want will need to look something more like this:
class campaignProductSerializer(serializers.HyperlinkedModelSerializer):
product_name = serializers.CharField(source='product_id.name')
class Meta:
model = campaign_product
fields = ('product_id', 'campaign_id', 'product_name')
P.S. As an unrelated side note, it is generally a convention in Python code to name classes with CamelCase, such as Campaign, CampaignProduct, Product, and CampaignProductSerializer.
Edit: P.P.S. Originally, I had put written the product_name field with source='product.name'. This was actually due to me looking at the code too quickly and making assumptions based on Django conventions. Typically, with a Django ForeignKey, you would name the ForeignKey field after the model you are linking to, rather than explicitly naming it with _id. For example, the CampaignProduct model would typically be written with product = ForeignKey(...) and campaign = ForeignKey(...). In the background, Django will actually use product_id and campaign_id as the database field names. You also have access to those names on your model instances. But the product and campaign variables on your model instances actually return the objects which you are referring to. Hopefully that all makes sense.

Correct way of getting count on FK

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.

Reference Abstract Model in Join Table for Many-to-Many in django

I have the following models, with publication needing a m2m with authors via the join table specified, I have done this but keep getting the error:
Error: One or more models did not validate:
publications.workshop: 'staff' is a manually-defined m2m relation through model AuthorsJoinTable, which does not have foreign keys to Staff and Workshop
publications.technicalreport: 'staff' is a manually-defined m2m relation through model AuthorsJoinTable, which does not have foreign keys to Staff and TechnicalReport
publications.authorsjointable: 'publication' has a relation with model Publication, which has either not been installed or is abstract.
publications.authorsjointable: "unique_together" refers to staff, a field that doesn't exist. Check your syntax.
My models look like:
class Publication(models.Model):
title = models.CharField(max_length=500)
staff = models.ManyToManyField("personnel.Staff", related_name='%(app_label)s_%(class)s_related', through='AuthorsJoinTable')
tag = models.ManyToManyField("Tag", related_name='%(app_label)s_%(class)s_related')
class Meta:
abstract = True
class Workshop(Publication):
location = models.CharField(max_length=100)
workshop_title = models.CharField(max_length=100)
start_date = models.DateField()
end_date = models.DateField()
def __unicode__(self):
return u'%s - %s' % (self.title, self.workshoptitle)
class TechnicalReport(Publication):
published_date = models.DateField()
class AuthorsJoinTable(models.Model):
author = models.ForeignKey("Author", related_name='%(app_label)s_%(class)s_from')
publication = models.ForeignKey("Publication", related_name='%(app_label)s_%(class)s_to')
order = models.IntegerField()
class Meta:
unique_together = ('staff', 'publication')
class Tag(models.Model):
tag_name = models.CharField(max_length=100, primary_key=True)
class Author(models.Model):
name = models.CharField(max_length=100)
biography = models.TextField()
So how can I resolve this problem?
publications.authorsjointable: "unique_together" refers to staff, a field that doesn't exist. Check your syntax.
You can't create a ForeignKey on absract model because that model does not have a table in DB and therefore does not have primary key to reference. So you should make your Publication non-abstract or reference Workshop instead. Other error lines should also be gone after that.