Malformed models - but I dont see how - django

I keep getting these inner join errors when I try to delete an object. It's a programming error, and an exception value of "column main_reviewerauthoranswer.screener_id does not exist
LINE 1: ...viewerauthoranswer" INNER JOIN "main_reader" ON ( "main_revi..."
I'm using south. I'm migrating just fine. Creating instances just fine. But if I try to delete something I keep getting those errors. I don't know if something changed in django 1.6 with through tables? I'm very confused.
So this is what I"m doing. Empty database. I create a reader in the admin, I give the reader a genre ( I create it in the admin). Save. No problem. Ok, try to delete the reader I get
column main_reviewerauthoranswer.screener_id does not exist
LINE 1: ...viewerauthoranswer" INNER JOIN "main_reader" ON ( "main_revi...
If i try to delete the genre I just created for that reader I get a
column main_reviewerauthoranswer.screener_id does not exist
LINE 1: ...viewerauthoranswer" INNER JOIN "main_reader" ON ( "main_revi...
So obviously there is something wrong with my ReviewAuthorAnswer model. But I dont' see what
My models looks like this:
class DateDocumentation(models.Model):
modified_at = models.DateTimeField(auto_now=True)
created_at = models.DateTimeField(auto_now_add=True)
class Meta:
abstract = True
class Author(DateDocumentation):
user = models.OneToOneField(User, null=True)
class Reader(DateDocumentation):
user = models.OneToOneField(User)
favorite_genre = models.ForeignKey('Genre')
class Genre(models.Model):
title = models.CharField(max_length=100
class Book(DateDocumentation):
author = models.ForeignKey('Author')
book = models.FileField(upload_to='books/')
genre = models.ForeinKey('Gengre')
class Blurbreview(DateDocumentation):
reader = models.ForeignKey("Reader")
review = models.ForeignKey("Review")
class Review(DateDocumentation):
book = models.ForeignKey('Book')
blurb_readers = models.ManyToManyField("Reader",
through="blurbreview",
related_name='blurb_readers')
readers = models.ManyToManyField("Reader",
help_text="Readers that downloaded this book",)
class BaseQuestion(DateDocumentation):
review = models.ForeignKey("Review")
reviewer = models.ForeignKey("Reader")
class AuthorQuestion(DateDocumentation):
review = models.ForeignKey('Review')
class ReviewerAuthorAnswer(DateDocumentation):
question = models.ForeignKey('AuthorQuestion')
screener = models.ForeignKey('Reader')

You may have to rebuild the database. When you import new data, make sure that you run foreign_key_check=0; before the query.
I ran into this issue several times when I mistakenly tried importing data into an emptied database.

Related

How do I attach a field from a related object to a Django queryset?

We are on Django 1.5 (for now, until the boss can be convinced otherwise...)
The app has multiple objects that reference each other as foreign keys:
class Result(models.Model):
quiz = models.ForeignKey(Quiz)
user = models.ForeignKey(User)
class Quiz(models.Model):
quiz_type = models.CharField(max_length=1, choices=TYPE_CHOICES, default=TYPE_POST)
name = models.CharField(max_length=200)
lesson = models.ForeignKey(Lesson, blank=True, null=True, db_index=True)
class Lesson(models.Model):
title = models.CharField(max_length=200)
unit = models.ForeignKey(Unit)
class LessonStatus(models.Model):
user = models.ForeignKey(User)
lesson = models.ForeignKey(Lesson)
attempts_remaining = models.IntegerField(default=1)
passed = models.PositiveIntegerField(default=0)
For reporting, I need to get all Results for a specified user (or users), apply some filters based on user input, and then display a table with data that includes fields from User, Quiz, Lesson, and Unit. (It also checks LessonStatus for some display options.)
Right now, most of the foreign key queries are being done in a loop in the template, which is.. bad. I tried using select_related - my query log shows that it is successfully doing the table joins, but doesn't actually assign the fields to anything, so it seems that when I reference a related field, it's still causing a new query.
I have found similar questions, where the answers suggested using annotate, but that's not working for me.
I have tried:
r = Result.objects.filter(user__in=students).filter(~Q(quiz__quiz_type='3'))
r.annotate(qt=F('quiz__quiz_type'))
but that gets me : 'F' object has no attribute 'lookup'. This is the same whether or not I do a select_related('quiz'). What am I doing wrong?
Adding code from view:
r = Result.objects.filter(user__in=students).filter(~Q(quiz__quiz_type='3'))
r.select_related('quiz').select_related('user').select_related('lesson').select_related('unit')
if test_type != 'all':
r = r.filter(Q(quiz__quiz_type=test_type))
quiz__quiz_type='3')
if grade != 'any':
r = r.filter(Q(quiz__lesson__unit__grades=grade))
#more filters
for test in r:
if test.quiz.quiz_type == "1":
tu = test.user_id
try:
status = LessonStatus.objects.get(user=tu, lesson=test.quiz.lesson.id)
My expectation was that when I do the select_related, it shouldn't need to do additional queries for the related objects. However, I'm seeing queries on Quiz in that forloop, so I'm obviously doing something wrong.
Template:
(in a loop of for result in results)
{{result.user.first_name|title}} {{result.user.last_name|title}}
{{ result.quiz.lesson.title }}
{{ result.quiz.lesson.unit.title }}
{{ result.quiz.get_quiz_type_display }}
Query log is showing queries for each lesson and unit, so again, I'm doing something horribly wrong..
You need to follow the relationships on the select_related call itself; lesson and unit are not fields on Result, so will be ignored. It should be:
Result.objects.filter(...).select_related("quiz__lesson__unit", "user")

Adding to model with OneToMany and updating existing entries.

I have a couple of Django model questions. I am running the following code as a Django manage extension, which I am new to.
1) I am not certain my "Location.objects.get(city=key)" is correct for OneToMany in my add_server_to_db function. I suspect this is incorrect?
2) How can I harden this so if this is executed twice, it will update existing Server entries vs. error out?
Django Server Model:
class Servers(models.Model):
name = models.CharField(('name'), max_length=128)
location = models.OneToOneField('locations.Location', on_delete=models.CASCADE)
ip_address = models.CharField(('ip_address'), max_length=128)
date = models.DateField(auto_now=True)
Django Location Model:
class Location(models.Model):
city = models.CharField(('city'), max_length=10)
geolocation = models.PointField(('location'))
Function:
def add_server_to_db(data_dict):
print(data_dict)
for key, val in data_dict.items():
loc = Location.objects.get(city=key)
m = Server(
location=loc,
name=val['name'],
ip_address=val['ip_address'],
m.save()
Thanks.
I don't understand your question 1; there's nothing wrong with that line, and nothing specific to "one-to-many" in any case.
To prevent this creating new entries every time, you should use update_or_create:
loc = Location.objects.get(city=key)
Server.objects.update_or_create(
location=loc,
defaults={'name': val['name'], 'ip_address': val['ip_address']}
)
There's no need to call save() after this.

Changes to model with one to one relation not saving

I have two models that I'm relating using Django's OneToOneField, following this documentation: https://docs.djangoproject.com/en/2.0/topics/db/examples/one_to_one/
class Seats(models.Model):
north = models.OneToOneField('User',on_delete=models.CASCADE,related_name='north', default=None, null=True)
bridgetable = models.OneToOneField('BridgeTable',on_delete=models.CASCADE, default=None, null=True)
class BridgeTableManager(models.Manager):
def create_deal(self):
deal = construct_deal()
table = self.create(deal=deal)
s = Seats(bridgetable=table)
s.save()
return table
class BridgeTable(models.Model):
deal = DealField(default=None,null=True)
When I run this code I can successfully get the relationship working
table = BridgeTable.objects.get(pk='1')
user = User.objects.get(username=username)
table.seats.north = user
table.seats.north.save()
print(table.seats.north)
The print statement prints out the name of the player sitting north. But if I try to access the table again like this:
table = BridgeTable.objects.get(pk='1')
print(table.seats.north)
I get "None" instead of the user's name. Is there something I'm missing, like a save that I missed or some concept I'm not understanding? Thanks.
You should save Seats model object that is table.seats.save()
Try print table.seats.north
While table.seats.north.save() runs save on User object
Here are correct steps:
table = BridgeTable.objects.get(pk='1')
user = User.objects.get(username=username)
table.seats.north = user
table.seats.save()
print(table.seats.north)

How to join 2 tables in django

I have 2 models which i wanna join
class CollectionBook(models.Model):
collection = models.ForeignKey('Collection')
book = models.ForeignKey('Book')
class Meta:
unique_together = (('collection', 'book'))
class Book(models.Model):
main_author = models.ForeignKey('Author', verbose_name=u'Main author')
publisher = models.ForeignKey('Publisher', verbose_name=u'Publisher')
title = models.CharField(unique=False, max_length=255, verbose_name=u'Title')
text = models.TextField(verbose_name=u'Book text', max_length=523000)
I tried to do it this way
book = Book.objects.extra(
select = {'id':'mainapp_collectionbook.book_id'})
book.query.join((None,'mainapp_collectionbook',None,None))
connection = (
Book._meta.db_table,
CollectionBook.book.field,
)
book.query.join(connection, promote=True)
But it didn't work out and gave me error
Could you offer me another pythonic solutions of this problem or improve my way of doing it, I don't wanna write sql query, I hope that there are better django orm functions for this
Taking the clarification from the comment:
I have another table "Collection". I want select books where collection to which book belongs is in allowed collection list. I generate collection list before this query
First, replace your explicit CollectionBook table with a ManyToManyField on either Book or Collection. For this example I'll assume it's on Book, since that keeps the syntax clearer and the Collection model isn't shown.
class Book(models.Model):
main_author = models.ForeignKey('Author', verbose_name=u'Main author')
publisher = models.ForeignKey('Publisher', verbose_name=u'Publisher')
title = models.CharField(unique=False, max_length=255, verbose_name=u'Title')
text = models.TextField(verbose_name=u'Book text', max_length=523000)
collection = models.ManyToManyField('Collection')
Then you can use __ syntax to follow relationships:
Books.objects.filter(collection__in=SOME_LIST_OF_COLLECTIONS).distinct()
If you need additional information on the book/collection relation, EG a date collected or something, specify a through argument to the ManyToManyField.
if I understand correctly, you can try it, but don't forget to change the YOU_CONDITION
allow_collections = CollectionBook.objects.filter(YOU_CONDITION)
books_pks = allow_collections.values_list('book__pk', flat=true)
Book.objects.filter(pk__in=books_pks)

Django - Displaying result information while optimizing database queries with models that multiple foreign key relationships

So I'm trying to put together a webpage and I am currently have trouble putting together a results page for each user in the web application I am putting together.
Here are what my models look like:
class Fault(models.Model):
name = models.CharField(max_length=255)
severity = models.PositiveSmallIntegerField(default=0)
description = models.CharField(max_length=1024, null=False, blank=False)
recommendation = models.CharField(max_length=1024, null=False, blank=False)
date_added = models.DateTimeField(_('date added'), default=timezone.now)
...
class FaultInstance(models.Model):
auto = models.ForeignKey(Auto)
fault = models.ForeignKey(Fault)
date_added = models.DateTimeField(_('date added'), default=timezone.now)
objects = FaultInstanceManager()
...
class Auto(models.Model):
label = models.CharField(max_length=255)
model = models.CharField(max_length=255)
make = models.CharField(max_length=255)
year = models.IntegerField(max_length=4)
user = models.ForeignKey(AUTH_USER_MODEL)
...
I don't know if my model relationships are ideal, however it made sense it my head. So each user can have multiple Auto objects associated to them. And each Auto can have multiple FaultInstance objects associated to it.
In the results page, I want to list out the all the FaultInstances that a user has across their Autos. And under each listed FaultInstance I will have a list of all the autos that the user owns that has the fault, with its information (here is kind of what I had in mind).
All FaultInstance Listing Ordered by Severity (large number to low number)
FaultInstance:
FaultDescription:
FaultRecommendation:
ListofAutosWithFault:
AutoLabel AutoModel AutoYear ...
AutoLabel AutoModel AutoYear ...
Obviously, do things the correct way would mean that I want to do as much of the list creation in the Python/Django side of things and avoid doing any logic or processing in the template. I am able to create a list per severity with the a model manager as seen here:
class FaultInstanceManager(models.Manager):
def get_faults_by_user_severity(self, user, severity):
faults = defaultdict(list)
qs_faultinst = self.model.objects.select_related().filter(
auto__user=user, fault__severity=severity
).order_by('auto__make')
for result in qs_faultinst:
faults[result.fault].append(result)
faults.default_factory = None
return faults
I still need to specify each severity but I guess if I only have 5 severity levels, I can create a list for each severity level and pass each individual one to template. Any suggestions for this is appreciated. However, thats not my problem. My stopping point right now is that I want to create a summary table at the top of their report which can give the user breakdown of fault instances per make|model|year. I can't think of the proper query or data structure to pass on to the template.
Summary (table of all the FaultInstances with the following column headers):
FaultInstance Make|Model|Year NumberOfAutosAffected
This will let me know metrics for a make or a model or a year (in the example below, its separating faults based on model). I'm listing FaultInstances because I'm only listed Faults that a connected to a user.
For Example
Bad Starter Nissan 1
Bad Tailight Honda 2
Bad Tailight Nissan 1
And I am such a perfectionist that I want to do this while optimizing database queries. If I can create a data structure in my original query that will be easily parsed in template and still get both these sections in my report (maybe a defaultdict of a defaultdict(list)), thats what I want to do. Thanks for the help and hopefully my question is thorough and makes sense.
It makes sense to use related names because it simplifies your query. Like this:
class FaultInstance(models.Model):
auto = models.ForeignKey(Auto, related_name='fault_instances')
fault = models.ForeignKey(Fault, related_name='fault_instances')
...
class Auto(models.Model):
user = models.ForeignKey(AUTH_USER_MODEL, related_name='autos')
In this case you can use:
qs_faultinst = user.fault_instances.filter(fault__severity=severity).order_by('auto__make')
instead of:
qs_faultinst = self.model.objects.select_related().filter(
auto__user=user, fault__severity=severity
).order_by('auto__make')
I can't figure out your summary table, may be you meant:
Fault Make|Model|Year NumberOfAutosAffected
In this case you can use aggregation. But It (grouping) would still be slow if you have enough data. The one easy solution is just to denormalize data by creating extra model and create few signals to update it or you can use cache.
If you have a predefined set of severities then think about this:
class Fault(models.Model):
SEVERITY_LOW = 0
SEVERITY_MIDDLE = 1
SEVERITY_HIGH = 2
...
SEVERITY_CHOICES = (
(SEVERITY_LOW, 'Low'),
(SEVERITY_MIDDLE, 'Middle'),
(SEVERITY_HIGH, 'High'),
...
)
...
severity = models.PositiveSmallIntegerField(default=SEVERITY_LOW,
choices=SEVERITY_CHOICES)
...
In your templates you can just iterate through Fault.SEVERITY_CHOICES.
Update:
Change your models:
Аllocate model into a separate model:
class AutoModel(models.Model):
name = models.CharField(max_length=255)
Change the field model of model Auto :
class Auto(models.Model):
...
auto_model = models.ForeignKey(AutoModel, related_name='cars')
...
Add a model:
class MyDenormalizedModelForReport(models.Model):
fault = models.ForeignKey(Fault, related_name='reports')
auto_model = models.ForeignKey(AutoModel, related_name='reports')
year = models.IntegerField(max_length=4)
number_of_auto_affected = models.IntegerField(default=0)
Add a signal:
def update_denormalized_model(sender, instance, created, **kwargs):
if created:
rep, dummy_created = MyDenormalizedModelForReport.objects.get_or_create(fault=instance.fault, auto_model=instance.auto.auto_model, year=instance.auto.year)
rep.number_of_auto_affected += 1
rep.save()
post_save.connect(update_denormalized_model, sender=FaultInstance)