Here are the relevant models:
class Event(models.Model):
objects = InheritanceManager()
game = models.ForeignKey(Game)
time = models.DateTimeField(auto_now_add=True)
class Meta:
ordering = ['time']
class ShipMoveEvent(Event):
objects = InheritanceManager()
ship = models.ForeignKey(Ship)
space = space(null=True, blank=True)
class DepthChargeEvent(ShipMoveEvent):
target = space()
nearMiss = models.PositiveSmallIntegerField()
hit = models.ForeignKey(Sub, blank=True, null=True)
I can create a ShipMoveEvent fine. When I go to create the DepthChargeEvent, I get this error on save:
django.db.utils.IntegrityError: NOT NULL constraint failed: sub_search_depthchargeevent.event_ptr_id
Any ideas why? I am stumped. It obviously has something to do with django's multi-table inheritance.
It seems that this is a bug in the sqlite database binding. I switched to postgresql and have not had this problem since.
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.
Below models.py to build a blog in wagtail site is from this post.
class BlogPage(Page):
description = models.CharField(max_length=255, blank=True,)
content_panels = Page.content_panels + [FieldPanel("description", classname="full")]
class PostPage(Page):
header_image = models.ForeignKey(
"wagtailimages.Image",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
)
tags = ClusterTaggableManager(through="blog.PostPageTag", blank=True)
content_panels = Page.content_panels + [
ImageChooserPanel("header_image"),
InlinePanel("categories", label="category"),
FieldPanel("tags"),
]
class PostPageBlogCategory(models.Model):
page = ParentalKey(
"blog.PostPage", on_delete=models.CASCADE, related_name="categories"
)
blog_category = models.ForeignKey(
"blog.BlogCategory", on_delete=models.CASCADE, related_name="post_pages"
)
panels = [
SnippetChooserPanel("blog_category"),
]
class Meta:
unique_together = ("page", "blog_category")
#register_snippet
class BlogCategory(models.Model):
name = models.CharField(max_length=255)
slug = models.SlugField(unique=True, max_length=80)
panels = [
FieldPanel("name"),
FieldPanel("slug"),
]
def __str__(self):
return self.name
class Meta:
verbose_name = "Category"
verbose_name_plural = "Categories"
class PostPageTag(TaggedItemBase):
content_object = ParentalKey("PostPage", related_name="post_tags")
#register_snippet
class Tag(TaggitTag):
class Meta:
proxy = True
I am wondering, what are the major reasons to introduce extra Intermediary model (class PostPageBlogCategory(models.Model): & class PostPageTag(TaggedItemBase):) to link PostPage to Category & Tag?
Why not just simply use ParentalForeignkey or ParentalManyToManyKey ?
The short answer is that Django's ManyToManyField has some limitations in how the relationship needs to be built when models on either end to not yet exist, adding an 'intermediary model' helps to work around this.
Longer Answer
As with anything in software, there are multiple ways to do something and each has its own pros & cons. Django's built in field is simple and lets you do lots of powerful things to represent relational data but that simplicity comes with some reduced flexibility in how these relationships are managed.
One of the main goals of the Wagtail CMS Admin interface is the ability to work as though the data has been created (including relationships) before actually clicking 'save'. While this may seem simple at first glance, getting to that point requires a bit of nuance under the hood once you start to consider relational data.
Wagtail comes built in with a very powerful library called django-modelcluster which has been purpose built for many of the cases where you want to work with relational data without having all the bits in the DB first.
Each Wagtail Page actually inherits the modelcluster.models.ClusterableModel, which is why some of the features in the blog post seem to work in the editor, even when the DB entries have not yet been saved.
On the blog post you linked, there is a section towards the end with the heading 'ParentalKey' that further explains this nuance and how just using Django's basic approach has some draw backs.
On the Django docs for many-to-many relationships, have a read through and note that each individual model instance must be in the database first and only then can you 'link' the two instances with a second update on each.
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
i am trying to make a simple profile edit form for users on a website. I've followed the standard advice for updating, in the docs it says that Django detects the instances primary key and knows to update instead of insert.
only problem is, i get an insert when i am trying to update. I pre populate a form with a model instance (the instance that im trying to edit) but when i try and save it, i get a new instance. When i add the 'force_update=True' line, i get an error message that tells me that no primary key is detected. Not sure why, because im pre populating the form with a model instance, although, obviously the pk is not a part of the form. is there something im missing?
some code:
the model:
class profile(models.Model):
user = models.ForeignKey(User)
first_name = models.CharField(max_length=20, null=True)
last_name = models.CharField(max_length=20, null=True)
DOB = models.DateField(null=True)
age = models.IntegerField(null=True)
public_email = models.EmailField(null=True)
county = models.CharField(max_length=20, null=True)
town = models.CharField(max_length=30, null=True)
the form:
class profileForm(forms.ModelForm):
class Meta:
model = profile
exclude = ['user']
the view:
#login_required()
def edit_profile(request):
if request.POST:
proform = profileForm(request.POST)
if proform.is_valid():
prof = proform.save(False)
prof.user = request.user
prof.save(force_update=True)
return HttpResponseRedirect('/accounts/view_profile/')
else:
c = {}
if profile.objects.filter(user=request.user).exists():
prof = profile.objects.get(user=request.user)
c['proform'] = profileForm(instance=prof)
else:
c['proform'] = profileForm()
return render(request, 'edit_profile.html', c)
any help greatly appreciated!
i got it, turns out i was trying to just calling save() on the form without specifying the particular instance that the form relates to.
code:
#login_required()
def edit_profile(request):
c = {}
if profile.objects.filter(user=request.user).exists():
profModel = profile.objects.get(user=request.user)
c['proform'] = profileForm(instance=profModel)
else:
c['proform'] = profileForm()
if request.POST:
# this line here, added 'instance=profModel' to specify
# the actual instance i want to save
proform = profileForm(request.POST, instance=profModel)
if proform.is_valid():
prof = proform.save(False)
prof.user = request.user
prof.save()
return HttpResponseRedirect('/accounts/view_profile/')
else:
return render(request, 'edit_profile.html', c)
works!
I'm playing with GeoDjango and have some doubts. I'll really appreciate any comment and suggestion.
This is my problem. First, I've defined this (abstract) class:
from django.contrib.gis.db import models
from django.contrib.gis.geos import *
class LocatableModel(models.Model):
country = models.CharField(max_length=48, blank=True)
country_code = models.CharField(max_length=2, blank=True)
locality = models.CharField(max_length=48, blank=True)
sub_locality = models.CharField(max_length=48, blank=True)
street = models.CharField(max_length=48, blank=True)
address = models.CharField(max_length=120, blank=True)
point = models.PointField(null=True)
objects = models.GeoManager()
class Meta:
abstract = True
Second, I've defined this other 'Entity' class, which
represents a person or organization related to my site:
from django.db import models
class Entity(models.Model):
name = models.CharField(max_length=64)
slug = models.SlugField(max_length=64, unique=True)
website = models.URLField(verify_exists=False, blank=True)
email = models.EmailField(blank=True)
...
Finally, I've created a class from the previous ones:
import LocatableModel
import Entity
class Organization(Entity, LocatableModel):
timetable = models.CharField(max_length=64)
...
In my views, I'd like to find organizations near a specific point:
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import D
def index(request):
pnt = Point(12.4604, 43.9420)
dic = { 'orgs': Organization.objects.filter(point__distance__lte=(pnt, D(km=7))) }
return render_to_response('index.html', dic)
But I receive the error:
"Join on field 'point' not permitted. Did you misspell 'distance' for
the lookup type?"
I think I'm doing a mess with the model 'objects' property, but I'm not sure. Any ideas?
Thanks in advance.
This error has been seen before, and claimed to be solved in this ticket 3 years ago:
https://code.djangoproject.com/ticket/9364
When I ran into this same problem, I noticed in the ticket that the query manager was set explicitly to GeoManager in the inherited model(s). So adding a line like,
class Organization(Entity, LocatableModel):
timetable = models.CharField(max_length=64)
...
objects = models.GeoManager()
...may solve the issue you're seeing, it worked for me.