mypy and Django type checking - python

I'm trying to enable type hints for my Django REST project. I installed django-stubs and djangorestframework-stubs and I have the following mypy.ini file:
[mypy]
plugins =
mypy_django_plugin.main
mypy_drf_plugin.main
[mypy.plugins.django-stubs]
django_settings_module = "core.settings.base"
Some of the type hints do work; for example, if I have a variable inside of a method whose type is a model class, I get hints when I try to access a field or method defined on it.
However, if I try to access a Django-specific field on the model, for example a reverse relation, that does not typecheck and gives me an error. Moreover, if I try to access fields on a related model of my model variable, the related object has type Any
For example, with these two models:
class User(models.Model):
name = models.TextField()
role = models.ForeignKey(Role) # definition of Role model not relevant
class Badge(models.Model):
user = models.ForeignKey(User, related_name="badges")
then this will happen:
u: User = get_user()
print(u.name) # correctly hinted and typechecks
print(u.badges.all()) # type error
print(u.role.pk) # no hint on role.pk, role is Any
How do I get my project to correclty type check all the Django-specific features such as foreign key fields, querysets, and reverse relationships?

Related

Reverse accessor clasher with Django [duplicate]

D:\zjm_code\basic_project>python manage.py syncdb
Error: One or more models did not validate:
topics.topic: Accessor for field 'content_type' clashes with related field 'Cont
entType.topic_set'. Add a related_name argument to the definition for 'content_t
ype'.
topics.topic: Accessor for field 'creator' clashes with related field 'User.crea
ted_topics'. Add a related_name argument to the definition for 'creator'.
topics.topic: Reverse query name for field 'creator' clashes with related field
'User.created_topics'. Add a related_name argument to the definition for 'creato
r'.
topicsMap.topic: Accessor for field 'content_type' clashes with related field 'C
ontentType.topic_set'. Add a related_name argument to the definition for 'conten
t_type'.
topicsMap.topic: Accessor for field 'creator' clashes with related field 'User.c
reated_topics'. Add a related_name argument to the definition for 'creator'.
topicsMap.topic: Reverse query name for field 'creator' clashes with related fie
ld 'User.created_topics'. Add a related_name argument to the definition for 'cre
ator'.
You have a number of foreign keys which django is unable to generate unique names for.
You can help out by adding "related_name" arguments to the foreignkey field definitions in your models.
Eg:
content_type = ForeignKey(Topic, related_name='topic_content_type')
See here for more.
http://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey.related_name
Example:
class Article(models.Model):
author = models.ForeignKey('accounts.User')
editor = models.ForeignKey('accounts.User')
This will cause the error, because Django tries to automatically create a backwards relation for instances of accounts.User for each foreign key relation to user like user.article_set. This default method is ambiguous. Would user.article_set.all() refer to the user's articles related by the author field, or by the editor field?
Solution:
class Article(models.Model):
author = models.ForeignKey('accounts.User', related_name='author_article_set')
editor = models.ForeignKey('accounts.User', related_name='editor_article_set')
Now, for an instance of user user, there are two different manager methods:
user.author_article_set — user.author_article_set.all() will return a Queryset of all Article objects that have author == user
user.editor_article_set — user.editor_article_set.all() will return a Queryset of all Article objects that have editor == user
Note:
This is an old example — on_delete is now another required argument to models.ForeignKey. Details at What does on_delete do on Django models?
"If a model has a ForeignKey, instances of the foreign-key model will have access to a Manager that returns all instances of the first model. By default, this Manager is named FOO_set, where FOO is the source model name, lowercased."
But if you have more than one foreign key in a model, django is unable to generate unique names for foreign-key manager.
You can help out by adding "related_name" arguments to the foreignkey field definitions in your models.
See here:
https://docs.djangoproject.com/en/dev/topics/db/queries/#following-relationships-backward
If your models are inheriting from the same parent model, you should set a unique related_name in the parent's ForeignKey. For example:
author = models.ForeignKey('accounts.User', related_name='%(app_label)s_%(class)s_related')
It's better explained in th
If your models are inheriting from the same parent model, you should set a unique related_name in the parent's ForeignKey. For example:
author = models.ForeignKey('accounts.User', related_name='%(app_label)s_%(class)s_related')
It's better explained in the docs
I had a similar problem when I was trying to code a solution for a table that would pull names of football teams from the same table.
My table looked like this:
hometeamID = models.ForeignKey(Team, null=False, on_delete=models.CASCADE)
awayteamID = models.ForeignKey(Team, null=False, on_delete=models.CASCADE)
making the below changes solved my issue:
hometeamID = models.ForeignKey(Team, null=False, on_delete=models.CASCADE,related_name='home_team')
awayteamID = models.ForeignKey(Team, null=False, on_delete=models.CASCADE,related_name='away_team')
But in my case i am create a separate app for some functionality with same model name and field ( copy/paste ;) ) that's because of this type of error occurs i am just deleted the old model and code will work fine
May be help full for beginners like me :)
This isn't an ultimate answer for the question, however for someone it may solve the problem.
I got the same error in my project after checking out a really old commit (going to detached head state) and then getting the code base back up to date. Solution was to delete all *.pyc files in the project.
Do as the error message instructs you to:
Add a related_name argument to the
definition for 'creator'.

Cannot delete some instances of model because of FK

Python 3.6 and Django 1.11.7.
I've got two Models look like the following:
class User():
name = models.CharField()
...
class UserInfo():
user = models.OneToOneField(User, on_delete=models.PROTECT, primary_key=True, related_name='info')
I wanted to delete some user instance A, and I explicitly deleted user A's info. But when I tried to delete the user model user.delete(), I got the ProtecedError:
ProtectedError: ("Cannot delete some instances of model 'User' because they are referenced through a protected foreign key: 'UserInfo.user'", <QuerySet [<UserInfo: UserInfo object>]>)
Then I tried to put the delete inside a try/catch looks like follows:
try:
user.delete()
except ProtectedError:
UserInfo.objects.filter(user=user).delete()
user.delete()
But still got the same exception. What might went wrong in my operation?
You are using a protect clause on the related objects:
on_delete=models.PROTECT
You can check it in the documentation on:
https://docs.djangoproject.com/en/2.2/ref/models/fields/#django.db.models.ForeignKey.on_delete
You have it pointed here:
PROTECT[source]
Prevent deletion of the referenced object by raising ProtectedError, a subclass of django.db.IntegrityError.
Remove the on_delete=models.PROTECT on your user field. And run manage.py makemigrations
ForeignKey fields have a default value of CASCADE for the on_delete argument. This means that deleting the user object will cascade down and also delete the userinfo object linked to that user.
Looks like this is the behaviour you are looking for.
You can read more about this in the documentation documentation
Also note although on_delete has a default value fo CASCADE this argument will be required from Django 2.0.

Django: something wrong with ManyToManyField.limit_choices_to

I have a model with ManyToManyField. This field has limit_choices_to attribute which is set to callable. My problem is that this callable isn't called every time form is validated/instantiated in my view. It is called only once, after making changes in code. Django says:
If a callable is used for limit_choices_to, it will be invoked every
time a new form is instantiated. It may also be invoked when a model
is validated, for example by management commands or the admin. The
admin constructs querysets to validate its form inputs in various edge
cases multiple times, so there is a possibility your callable may be
invoked several times.
Any ideas why it doesnt working?
My code:
def limit_name_choices():
if Name.objects.count():
return {"pk__gt": Name.objects.last().pk}
else:
return {}
class Robject(models.Model):
project = models.ForeignKey(to=Project, null=True, blank=True)
names = models.ManyToManyField(
"Name",
related_name="robject_names",
limit_choices_to= limit_name_choices,
blank=True
)
[...]
SOLVED:
Bug was fixed in 1.11.3 release.

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)

adding permissions to an object which is not an instance of Django User in Django

I added a Meta class which contains the following permissions but i don't know if its added just by only putting them in Meta class or i need to add something or assign them by a certain way. Here is my code :
class Contractor(models.Model):
contractor_verified = models.BooleanField(default=False)
Employer = models.OneToOneField(Employer)
job_title = models.CharField(max_length=50)
name = models.CharField(max_length=250)
class Meta:
permissions = (
("permission", "perm"),
("bid", "bid"),
("send_request_to_verifier", "send_request_to_verifier"),
("gain_points", "gain_points"),
("loose_points", "loose_points"),
)
and i am sorry if anything is not clear. I just need help on how to add custom permissions to an object which is not an instance of Django User
The django permissions system is only designed out of the box to support User and Group model permissions. From the django docs regarding object permissions: https://docs.djangoproject.com/en/1.3/topics/auth/#handling-object-permissions
Django's permission framework has a foundation for object permissions,
though there is no implementation for it in the core. That means that
checking for object permissions will always return False or an empty
list (depending on the check performed).
If you are looking for generic object-level permissions, there are a slew of available packages here: http://pypi.python.org/pypi?%3Aaction=search&term=django+permissions&submit=search
Some of these simply implement the model level permissions, and some let you define a per instance permission.

Categories