A trouble with Django models and index_together feature - python

Create a Django project with an app (add it to INSTALLED_APPS!) with the following models.py:
from django.db import models
class BaseTransaction(models.Model):
pass
class SubscriptionTransaction(BaseTransaction):
class Meta:
index_together = ["id", "canceled"]
canceled = models.BooleanField()
Then the things work this way:
$ python3 manage.py makemigrations
SystemCheckError: System check identified some issues:
ERRORS:
testprj.SubscriptionTransaction: (models.E016) 'index_together' refers to field 'id' which is not local to model 'SubscriptionTransaction'.
HINT: This issue may be caused by multi-table inheritance.
Please explain the reason of this error (there is no multi-table inheritance here) and how to make my code to work.
The problem happens with Django 1.10.3 with both Python 3.5.3 and Python 2.7.13. Is it a Django bug? What's about a workaround?

You are getting this error because the id you are using is in other table and this could be generate a lot of problems.
But if you don't need BaseTransaction table you can mark it as abstract, then you can use your index_together perfectly.
class BaseTransaction(models.Model):
class Meta:
abstract = True

Related

Django: Resolve a related field to QuerySet type

Based on the official documentation:
# Declare the ForeignKey with related_name
class Tag(models.Model):
article = models.ForeignKey(
Article,
on_delete=models.CASCADE,
related_name="tags"
)
name = models.CharField(max_length=255)
# Return all tags
Article.tags.all()
My linter (django-pylint) is unable to type it porperly: Article.tags is Any, I expected a QuerySet[Tag].
Can I declare the Article.tags reference in the Article class? (preferred approach)
from django.db.models.query import QuerySet
class Article(models.Model):
...
# Related field declaration
tags: QuerySet[Tag]
Article.tags.all()
Or maybe I need to convert it every time I need it?
tags_qs: QuerySet[Tag] = Article.tags
tags_qs.all()
In both scenarios, it looks heavy to implement for each related field.
Of course, it's more a question for comfortable development experience than a critical issue.
The goal is to allow my linter and other autocompletion/discovery tools to resolve related fields as QuerySet[T] type. Maybe I can't due to the design of the Python implementation, more than a Django issue.
Is there any other alternative to fix this problem?
Thanks to #Anentropic for pointing me in the right way, I opted for django-types. This library can be easily integrated with VS Code and the PyLance extension (which uses PyRight type checking, not MyPY).
Setup with VS Code
Clone django-types
Rename django_stubs to django
Move it into ./typings/django in your project folder
Adaptation
Following this instructions: https://github.com/sbdchd/django-types#usage
from __future__ import annotations # or just be in python 3.11
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from django.db.models import Manager
class Article(models.Model):
tags: Manager["Tag"]
# Declare the ForeignKey with related_name
class Tag(models.Model):
article = models.ForeignKey(
Article,
on_delete=models.CASCADE,
related_name="tags"
)
name = models.CharField(max_length=255)
# Return all tags
Article.tags.all()
Now, PyLance working fine with autocompletion!

Fields clash in case of inheritance

I' ve the following simplified model structure:
#common/models.py
class CLDate(models.Model):
active = models.BooleanField(default=True)
last_modified = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
#br/models.py
class Dokument(CLDate):
user = models.ForeignKey(User)
class Entity(CLDate):
dokument = models.ForeignKey(Dokument)
. Both class inherits from CLDate, and i' ve a OneToMany relation between them. When i try to migrate, i got the following error:
python manage.py makemigrations
SystemCheckError: System check identified some issues:
ERRORS:
br.Entity.dokument: (models.E006) The field 'dokument' clashes with the
field 'dokument' from model 'common.cldate'.
I can' t really get why is this structure a problem for Django hence the Entity is a totally different object than the Dokument. Could anyone explain me why, and how could i solve it with this structure? So both should inherit from CLDate and there should be this kind of relation between the 2 models from the br application.
I also tried to delete all the migration files, and solve it that way, but the same. Runserver gives also this error.
Django: 1.11.2
Python: 3.4.2
Debian: 8.8
.
Thanks.
If i rename the dokument property name in the Entity model, it works fine.
I' m also almost pretty the same layout was working previously (in previous Django versions).
Since you are using multi-table inheritance, Django creates an implicit one-to-one field from Dokument to CLDate. The reverse relation dokument from CLDate to Dokument is clashing with your Entity.dokument field.
If you don't want to rename your Entity.dokument field, then your other option is to explicitly define the parent link field from Dokument to CLDate and set related_name.
class Dokument(CLDate):
cl_date = models.OneToOneField(CLDate, parent_link=True, related_name='related_dokument')
user = models.ForeignKey(User)

Django Models: Common Ancestor Inheritance & Migration

I thought I would up my python game with Django a bit by developing a large scale business app for fun. I seen the need for a common ancestor approach to model inheritence and tried to implement it based on the official documentation. However, I keep getting this very annoying Message which I'm not sure what to do with.
Dj Version: Django 1.7
Py Version: Python 3.4.2
Message
$ python manage.py makemigrations
You are trying to add a non-nullable field 'businessentity_ptr' to business without a default; we can't do that (the database needs something to populate existing rows).
Please select a fix:
1) Provide a one-off default now (will be set on all existing rows)
2) Quit, and let me add a default in models.py
Models.py
class BusinessEntity(models.Model):
title = models.CharField(max_length=180)
def __str__(self):
return self.title
class Business(BusinessEntity):
description = models.TextField(max_length=600)
claimed = models.BooleanField(default=False)
slug = models.SlugField()
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __str__(self):
return self.description
What I've Tried, (which everyone will hate):
Deleting the DB & Re-migrating
setting a default value for all fields
Setting all fields to null = True
I have seen a hack around for this but I don't think it's a good approach. Maybe there is someone out there who understand Django Common Ancestors much better and point me in the right direction.
Since your parent model is intended to be abstract, you should mark it as such.
class BusinessEntity(models.Model):
title = models.CharField(max_length=180)
class Meta:
abstract = True
This prevents Django from creating a separate table for it, and therefore needing a _ptr field to point back to it from the subclass. Instead, the table for your subclass will be created to include the inherited field(s) directly.

Django Circular Model Dependency

I have a circular dependency in my Django models, such that model A has a foreign key reference to B, while B has a many-to-many reference to A. I've consulted other SO posts and have used the string model names instead of the actual classes, but to no avail. Here are abbreviated versions of my two classes:
User model
import listings.models
class User(models.Model):
...
favorites = models.ManyToManyField('listings.models.Listing')
Listing model
import users.models
class Listing(models.Model):
...
owner = models.ForeignKey('users.models.User')
Every time I attempt to run syncdb, it outputs the following error:
Error: One or more models did not validate: users.user: 'favorites'
has an m2m relation with model listings.models.Listing, which has
either not been installed or is abstract. listings.listing: 'owner'
has a relation with model users.models.User, which has either not
been installed or is abstract.
How do I resolve this without sacrificing the established relationship?
'listings.models.Listing' should be 'listings.Listing'
'users.models.User' should be 'users.User' (or 'auth.User' if you were to use django.contrib.auth.models.User)
Refer to official documentation for more.
You can just delete your imports because, you're not depend on them on code. You use just string with model name - it's not a dependency.
Also you should delete models - from your strings because you can refer to your model as app_name.model_name

Django admin site not displaying ManyToManyField relationship

I'm working on what I think is a pretty standard django site, but am having trouble getting my admin section to display the proper fields.
Here's my models.py:
class Tech(models.Model):
name = models.CharField(max_length = 30)
class Project(models.Model):
title = models.CharField(max_length = 50)
techs = models.ManyToManyField(Tech)
In other words, a Project can have different Tech objects and different tech objects can belong to different Projects (Project X was created with Python and Django, Project Y was C# and SQL Server)
However, the admin site doesn't display any UI for the Tech objects. Here's my admin.py:
class TechInline(admin.TabularInline):
model = Tech
extra = 5
class ProjectAdmin(admin.ModelAdmin):
fields = ['title']
inlines = []
list_display = ('title')
admin.site.register(Project, ProjectAdmin)
I've tried adding the TechInline class to the inlines list, but that causes a
<class 'home.projects.models.Tech'> has no ForeignKey to <class 'home.projects.models.Project'>
Error. Also tried adding techs to the fields list, but that gives a
no such table: projects_project_techs
Error. I verified, and there is no projects_project_techs table, but there is a projects_tech one. Did something perhaps get screwed up in my syncdb?
I am using Sqlite as my database if that helps.
I've tried adding the TechInline class to the inlines list, but that causes a
'TechInLine' not defined
Is that a straight copy-paste? It looks like you just made a typo -- try TechInline instead of TechInLine.
If your syncdb didn't create the proper table, you can do it manually. Execute this command:
python manage.py sqlreset <myapp>
And look for the definition for the projects_project_techs table. Copy and paste it into the client for your database.
Assuming your app is called "projects", the default name for your techs table will be projects_tech and the projects table will be projects_project.
The many-to-many table should be something like projects_project_techs
#John Millikin - Thanks for the sqlreset tip, that put me on the right path. The sqlreset generated code that showed me that the projects_project_techs was never actually created. I ended up just deleting my deb.db database and regenerating it. techs then showed up as it should.
And just as a sidenote, I had to do an admin.site.register(Tech) to be able to create new instances of the class from the Project page too.
I'll probably post another question to see if there is a better way to implement model changes (since I'm pretty sure that is what caused my problem) without wiping the database.

Categories