I'm getting a very weird error when I try any method (delete, save, etc) on a model that holds a Generic Foreign Key. My model can hold different type of card types, and a job to process them:
class JobCards(models.Model):
class Meta:
unique_together = ('content_type', 'object_id')
content_type = models.ForeignKey(
ContentType,
on_delete=models.CASCADE,
help_text=_('Card type')
)
object_id = models.PositiveIntegerField(
help_text=_('Card id')
)
content_object = GenericForeignKey()
job = models.ForeignKey(
Job,
on_delete=models.CASCADE
)
So operations like:
JobCards.objects.create(content_object=card, job=job)
or
job_card = JobCards.objects.get(
job=job,
content_type=ContentType.objects.get_for_model(card),
object_id=card.id
)
job_card.delete()
will fail with such error.
I should mention that I put two GenericRelation in the card models, but even removing them doesn't change the end result:
job_cards = GenericRelation(JobCards, related_query_name='card')
Solved.
Leaving this here just in case someone comes across the same issue. The problem was project related, not an issue with Django.
The ContentType models I was using required a specific field, and somehow with a generic foreign key that is lost (while it seems just fine with a normal foreign key).
Adding it as a property of the model did the trick:
#property
def required_field(self):
return self.content_object.some_linked_model.required_field
Related
I am building a blog website and I am using Django rest framework
I want to fetch top 2 comments for a particular post along with their related data such as user details.
Now I have user details in two models
User
People
and the comments model is related to the user model using foreign key relationship
Models ->
Comments
class Comment(models.Model):
comment = models.TextField(null=True)
Created_date = models.DateTimeField(auto_now_add=True)
Updated_date = models.DateTimeField(auto_now=True)
post = models.ForeignKey(Post, on_delete=models.CASCADE,
related_name='comments_post')
user = models.ForeignKey(User, on_delete=models.CASCADE,
related_name='comments_user')
The People model is also connected to the user model with a foreign key relationship
People Model ->
class People(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE,related_name='people')
Name = models.CharField(max_length=255,null=True)
following = models.ManyToManyField(to=User, related_name='following', blank=True)
photo = models.ImageField(upload_to='profile_pics', blank=True,null=True)
Phone_number = models.CharField(max_length=255,null=True,blank=True)
Birth_Date = models.DateField(null=True,blank=True)
Created_date = models.DateTimeField(auto_now_add=True)
Updated_date = models.DateTimeField(auto_now=True)
for fetching the comments I am using rest-framework and the serializers look like this
class UserSerializer(serializers.Serializer):
username = serializers.CharField(max_length=255)
class peopleSerializer(serializers.Serializer):
Name = serializers.CharField(max_length=255)
class commentsSerializer(serializers.Serializer):
id = serializers.IntegerField(read_only=True)
comment = serializers.CharField(max_length=255)
Created_date = serializers.DateTimeField()
user = UserSerializer()
people = peopleSerializer()
The query to fetch the comments look like this ->
post_id = request.GET.get('post_id')
comments = Comment.objects.filter(post_id=post_id).select_related('user').prefetch_related('user__people').order_by('-Created_date')[:2]
serializer = commentsSerializer(comments, many=True)
return Response(serializer.data)
I am getting this error ->
Got AttributeError when attempting to get a value for field `people` on serializer `commentsSerializer`. The serializer field might be named incorrectly and not match any attribute or key on the `Comment` instance. Original exception text was: 'Comment' object has no attribute 'people'.
Unable to find a way out.
The source is user.people, not people, so:
class commentsSerializer(serializers.Serializer):
# …
people = peopleSerializer(source='user.people')
In the .select_related(…) [Django-doc] to can specify user__people: this will imply selecting user and will fetch the data in the same query, not in an extra query as is the case for .prefetch_related(…) [Django-doc]:
post_id = request.GET.get('post_id')
comments = Comment.objects.filter(
post_id=post_id
).select_related('user__people').order_by('-Created_date')[:2]
serializer = commentsSerializer(comments, many=True)
return Response(serializer.data)
Note: normally a Django model is given a singular name, so Person instead of People.
Note: It is normally better to make use of the settings.AUTH_USER_MODEL [Django-doc] to refer to the user model, than to use the User model [Django-doc] directly. For more information you can see the referencing the User model section of the documentation.
Note: normally the name of the fields in a Django model are written in snake_case, not PascalCase, so it should be: created_date instead of Created_date.
I have this architecture (very simplified)
from django.db import Models
class MainClass(models.Model):
a = models.IntegerField()
b = models.CharField()
class OtherClass(models.Model):
c = models.IntegerField()
main = models.OneToOneField(MainClass, primary_key=True)
Which means my MainClass object has an attribute named otherclass, because of the existence of the reverse relationship between these models.
My problem is if I specify valid values for MainClass.a and MainClass.b, but None for MainClass.otherclass. I get the error
ValueError: Cannot assign None: "MainClass.otherclass" does not allow null values.
I understand there cannot be OtherClass without MainClass (it doesn't make sense), but why the opposite situation is also causing an error? Other way: Why cannot be MainClass without OtherClass?
Looks like this is a normal behaviour in Django 1.8, although the restriction has been removed in Django 1.10
So, This isn't an error.
All I am trying to produce is an entity that holds a unique username, and a unique device ID, and the ability to return an error if either of these conditions are not met on submission.
The only way I can see is to perform a query within a transaction, then filter the results. This however requires an ancestor (which seems unnecessary for a single simple entity).
What is the best method to go about doing this?
Here is an example that does what you want.
I put 2 entities to show you also how to make relationships
class Person(ndb.Expando):
registration_date = ndb.DateTimeProperty(auto_now_add=True)
#property
def info(self):
info = PersonInfo.query(ancestor=self.key).get()
return info
class PersonInfo(ndb.Expando):
email = ndb.StringProperty()
nick_name = ndb.StringProperty()
edit_date = ndb.DateTimeProperty(auto_now=True)
Later in the controller for register:
class RegisterPersonHandler(webapp2.RequestHandler):
def get(self):
user = users.get_current_user() #Stub here
if not user:
self.redirect(users.create_login_url(self.request.uri), abort=True)
return
person = Person.get_or_insert(user.user_id())
if not self._register(person, user):
# more logging is needed
logging.warning('Warning registration failed')
return
#ndb.transactional()
def _register(self, person, user):
''' Registration process happens here
'''
# check if the person has info and if not create it
info = PersonInfo.query(ancestor=person.key).get()
if not info:
info = PersonInfo(id=user.user_id(), parent=person.key)
info.nick_name = user.nickname()
info.email = user.email()
info.put()
return True
To answer also the comment question:
How can you programatically tell whether the returned entity is a new
or existing one though?
Try checking against a property that is default. Eg creation_date etc.
Though you can also check on something you need or on another entity's existence like I do because I expect the data to be consistent, and if not then create the bond.
I have a main table
Slideshow
then a site specific table that captures a few extra details for that site.
Site1_Slideshow
In a web app (specific to a site) i want a single model i.e. Slideshow that combines the 2 tables above.
Currently i have the code below, but i dont think this is correct. I cant do things like
s = Slideshow.objects.get(slideshowId=1) as Slideshows only has the properties featurecategory and slideshow. So how can i have an model called Slideshow that is composed of these 2 tables but looks like it was a single db table.
class SlideshowAbstract(models.Model):
slideshowid = models.IntegerField(primary_key=True, db_column=u'SlideshowId') # Field name made lowercase.
headline = models.TextField(db_column=u'Headline') # Field name made lowercase.
class Meta:
db_table = u'Slideshow'
class Slideshow(models.Model):
slideshow = models.OneToOneField(SlideshowAbstract, primary_key=True,db_column=u'SlideshowId')
def __unicode__(self):
return self.slideshow.headline
class Meta:
db_table = u'Site1_Slideshow'
Think i found the solution.
On the Site1_Slideshow you need to add a column for django to use, that i presume is always the same as primary key value.
Its name is SlideshowAbstract_ptr_id
Once that is added you can change the Slideshow model to be
class Slideshow(SlideshowAbstract):
featureCategory = models.ForeignKey(Featurecategory,db_column=u'FeatureCategoryId')
def __unicode__(self):
return self.headline
class Meta:
db_table = u'Site1_Slideshow'
So doable but not the nicest if you are not doing "model first" and already have the schema. Would be good to be able to override the name of the _ptr_id column.
I did try adding the following to Slideshow too see if i could map this ptr col to the primary key
slideshowabstract_ptr_id = models.IntegerField(primary_key=True, db_column=u'SlideshowId')
but no cigar.
I havent tested inserts either but ...objects.all() works
I want to be able to, given the key to a model in the datastore, simply set the referenceproperty without loading the model itself (as I don't need that information).
For example:
class Book(db.Model):
author = db.StringProperty()
title = db.StringProperty()
class Review(db.Model):
book = db.ReferenceProperty(Book)
description = db.StringProperty()
Assuming that I already have the key to a Book (call it bookKey), but I don't have the corresponding Book object itself, is there a way to do the equivalent of
review = Review()
review.description = "It was ok, but I would recommend it for insomniacs."
review.book = bookKey
or do I need to
book = Book.get(bookKey) #I don't want this datastore access
#as I don't need the book object.
review = Review()
review.description = "It was ok, but I would recommend it for insomniacs."
review.book = book
I've found the way to extract the key and ID from the ReferenceProperty using get_value_for_datastore, but I'm after a "set".
review.book = bookKey will work just fine, and set the referenceproperty without fetching the model.