Natural Key causes Django tests to fail - python

Consider a clean django 1.7.7 project with one app called testrunner.
The models look like this:
class Contact(AbstractBaseUser, PermissionsMixin, models.Model):
relation = models.ForeignKey('tests.Relation', related_name='contacts')
username = models.CharField(max_length=200, unique=True)
first_name = models.CharField(max_length=30, null=True, blank=True)
last_name = models.CharField(max_length=30, null=True, blank=True)
email = models.EmailField(null=True, blank=True, unique=True)
is_staff = models.BooleanField('staff status', default=False,
help_text='Designates whether the user can log into this admin site.')
is_active = models.BooleanField('active', default=True,
help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.')
USERNAME_FIELD = 'username'
def get_full_name(self):
pass
def get_short_name(self):
pass
def get_name(self):
pass
class Relation(models.Model):
name = models.CharField(max_length=200, unique=True)
created_by = models.ForeignKey('tests.Contact', null=True, related_name='created_%(class)s')
modified_by = models.ForeignKey('tests.Contact', null=True, related_name='modified_%(class)s')
def natural_key(self):
return (self.name,)
In the settings.py I've set 'tests.Contact' to by the AUTH_USER_MODEL.
This setup is a clean test to replicate the error I get within a larger environment. The problem is that I cannot run the django tests without it failing on the creation of the test database:
manage.py test
Testing started at 14:39 ...
Creating test database for alias 'default'...
CommandError: Can't resolve dependencies for tests.Contact, admin.LogEntry, tests.Relation in serialized app list.
Process finished with exit code 1
Empty test suite.
When I remove the def natural_key(self) from the Relation model everything works fine.
We would like to use the natural_key on the Relation model for our fixtures, but are unable to get it to work with django tests.
What am I doing wrong?

A very simple solution, yet took me some time to come up with.
tl;dr
Dynamically import serializers.
Dynamically define natural_keys method & assign to model.
def method_where_serializers_is_being_used():
# import serlializers
from django.core import serializers
# define natural_key method
def natural_key(self):
return some_unique_id
DjangoModel.natural_key = natural_key
It's a kind of a hack, but its working fine

The problem here is a circular dependency involving natural keys that Django fails to handle properly. Since this post was made, the loading of such data was already much improved, but serialization still breaks on a (now pretty obsolete) check for circular dependencies.
This is an open bug and reported here: https://code.djangoproject.com/ticket/31051

Related

How can I do authentication on the site and the built-in Users model and my own Employer Django?

I ran into a problem
I am making a job search site on Django, I have the following logic:
Authorization and authentication of ordinary job seekers using Django's built-in model - User
Also separate authorization and authentication for users who provide work, i.e. employers,
which are placed in my own model Employer
Here is my Employer model
class Employer(AbstractUser):
full_name = models.CharField(max_length=150, verbose_name="Ім'я")
main_office_city = models.ForeignKey(City, on_delete=models.CASCADE,
verbose_name='Місто головного офісу')
phone_number = models.ForeignKey(Phone, on_delete=models.CASCADE)
email = models.CharField(max_length=50, unique=True, verbose_name='Email')
hashed_password = models.CharField(max_length=120, default='')
date_joined = models.DateTimeField(verbose_name='Дата реєстрації',
default=timezone.now)
def __str__(self):
return self.full_name
class Meta:
verbose_name = 'Роботодавець'
verbose_name_plural = 'Роботодавці'
I read in the documentation that to create your own authentication system you can use the imitation from the AbstractUser class
But in my case this is not the best choice, because AbstractModel adds its own fields by default.
That is, I think that I need to either somehow make it so that the AbstractUser class does not add its fields, or think of some other authentication logic using another technology
Maybe someone has some ideas how it can be done?

How can solve a DeleteView problem in Django concerning a foreign key?

I have to apps (DMS and ObjPDW). The first one is for managing some files. In this I have a model DMS_Dokument, which includes a FileField and some more. Recently I added a new model to the latter app (ObjPDW) and I included a foreign key to Dokument_DMS:
class Zahlungsstrom(models.Model):
zahlung_bezeichnung = models.CharField(max_length=550, blank=False, null=False, verbose_name="Bezeichnung")
zahlung_betrag = models.DecimalField(max_digits=7, decimal_places=2, default=None, blank=True, null=True)
zahlung_dok_fk = models.ForeignKey(dmsdok.DMS_Dokument, on_delete=models.SET_DEFAULT, default=None, null=True, blank=True, verbose_name="Zahlungsdokument")
Now I wanted to delete a DMS_Dokument object (using the DeleteView CBV), but it gives me a "prorammingerror": "(1146, "Table 'DB_DMS.ObjPDW_zahlungsstrom' doesn't exist")"
I have no clue what the problem is. :(
Edit: Just to be clear on this. Both apps have their own databases. I know that Django is not recommending to relate models between two databases, but as I am no experienced programmer I do not know why I can make the relations work, but deletion is such a problem.
Furthermore I want to include some more code here, which is about the DMS_Dokument model. It also has a delete def.
class DMS_Dokument(models.Model):
dms_dok_titel = models.CharField(max_length=255, blank=True)
dms_dok_beschreibung = models.CharField(max_length=3000, blank=True, null=True)
dms_dok_datei = models.FileField(max_length=255,upload_to='DMS/')
dms_dok_gehoert_zu_app = models.CharField(max_length=255, choices=app_choices, blank=False, null=False)
def save(self, *args, **kwargs):
preserve_ext = extension(self.dms_dok_datei.name)
neuer_dateiname = self.dms_dok_gehoert_zu_app + '_' + self.dms_dok_titel + '_' + self.dms_dok_hochgeladen_am.strftime("%d.%m.%Y")
self.dms_dok_datei.name = neuer_dateiname + preserve_ext
super(DMS_Dokument, self).save(*args, **kwargs)
def delete(self):
self.indexes.all().delete()
super(DMS_Dokument, self).delete()
Perhaps this helps.
The error says the table does not exist. You must issue the following commands to create the migrations and make the table in you DB:
python manage.py makemigrations
python manage.py migrate

Problems extending my Django user model

I have a simple django app, and am trying to set up a custom Django User Model so that I can have users log in with their email field. I think I set everything up well enough, its all fairly straightforward, however when trying to actually run the migrations I get this error:
django.db.migrations.exceptions.InconsistentMigrationHistory: Migration admin.0001_initial is applied before its dependency accounts.0001_initial
Seems weird?
I'm having trouble understanding why it's having trouble understanding.
Heres the User model Im implementing
from __future__ import unicode_literals
from django.db import models
from django.core.mail import send_mail
from django.contrib.auth.models import PermissionsMixin
from django.contrib.auth.base_user import AbstractBaseUser
from django.utils.translation import ugettext_lazy as _
from .managers import UserManager
class User(AbstractBaseUser, PermissionsMixin):
email = models.EmailField(_('email address'), unique=True)
first_name = models.CharField(_('first name'), max_length=30, blank=True)
last_name = models.CharField(_('last name'), max_length=30, blank=True)
display_name = models.CharField(_('display name'), max_length=30, blank=True)
bio = models.TextField(blank=True)
date_joined = models.DateTimeField(_('date joined'), auto_now_add=True)
is_active = models.BooleanField(_('active'), default=True)
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
objects = UserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = []
class Meta:
verbose_name = _('user')
verbose_name_plural = _('users')
def get_full_name(self):
'''
Returns the first_name plus the last_name, with a space in between.
'''
full_name = '%s %s' % (self.first_name, self.last_name)
return full_name.strip()
def get_short_name(self):
'''
Returns the short name for the user.
'''
return self.first_name
def email_user(self, subject, message, from_email=None, **kwargs):
'''
Sends an email to this User.
'''
send_mail(subject, message, from_email, [self.email], **kwargs)
"Using a custom user model when starting a project"
If you’re starting a new project, it’s highly recommended to set up a custom user model, even if the default User model is sufficient for you. This model behaves identically to the default user model, but you’ll be able to customize it in the future if the need arises." reference->django docs
Then you have to specify to setting.py that you are using a new user model (again before you migrate for the first time.)
from django.contrib.auth.models import AbstractUser
class User(AbstractUser):
pass
# in settings.py
AUTH_USER_MODEL = 'myapp.MyUser'
I highly recommend checking out the link above.
Now to solve your problem you can delete your migrations in your app path under the migrations directory and reset your database and follow the instructions above.
Changing user model mid project is difficult in django and requires manually editing your schema so since your project is for educational purposes just reset it
Basically, you have already created some User accounts or at least have created superuser account. Since you are changing the User model after I assume you have already made migrations at least once, Django has conflict with old user model with the new one.
Solution is to delete all accounts in admin including superuser. Then delete your migrations files in migrations folder. And also I think delete your sqlite data file. Run migrations again and it should work.
You should have created custom user model before any migration.
One solution is to truncate the django_migrations table.
To do this you have to:
login to your database (In case you use Mysql) using mysql -u root -p
use your_db_name;
SET FOREIGN_KEY_CHECKS=0; to prevent relation errors
DELETE FROM django_migrations; truncates the django_migrations table
SET FOREIGN_KEY_CHECKS=1;
Then you can run your migrations!
Another solution is to run these commands before your migration to avoid migrations error:
python manage.py migrate admin zero --fake
python manage.py migrate auth zero --fake
python manage.py migrate contenttypes zero --fake
python manage.py migrate sessions zero --fake
Warning :
This solution may leave you with a database schema that's
inconsistent with the migrations applied, so only do this if
you know what you're doing. Thanks to #AKX
You can also use this solution too!

Django ForeignKey to AbstractBaseUser

I'm trying to set up an app that will handle reviews about registered users. So in my Review model, I want to have a ForeignKey to my User model.
I'm using a custom user profile that looks like this:
#In /profiles/models.py
class MyUser(AbstractBaseUser):
email = models.EmailField(
verbose_name='email address',
max_length=255,
unique=True,
)
company = models.CharField(default="", max_length=200)
is_active = models.BooleanField(default=True)
is_admin = models.BooleanField(default=False)
objects = MyUserManager()
USERNAME_FIELD = 'email'
REQUIRED_FIELDS = ['company']
I have included it with settings.py AUTH_USER_MODEL = "profiles.MyUser". It works fine with registration, creating users etc. So I know its working.
In my review model I write the following:
class Review(models.Model):
company = models.ForeignKey(settings.AUTH_USER_MODEL)
reviewer = models.ForeignKey(Reviewer)
rating = models.IntegerField(default=0)
review = models.TextField()
pub_date = models.DateTimeField('date published')
Instead of settings.AUTH_USER_MODEL I have also tried writing profiles.MyUser, 'profiles.MyUser' and MyUser.
I can successfully use the python manage.py makemigrations reviews command. But when I do python manage.py migrate I get errors no matter what version I use above.
The error I get is the following:
ValueError: Lookup failed for model referenced by field reviews.Review.company: profiles.MyUser
nejc92 comment was correct. I had migrated my database earlier before I set AUTH_USER_MODEL for the first time.
I removed my whole database and created new migrations for all apps and migrated everything again from scratch. It then worked.
Sounds like a bug(?) to me.

FieldError: Cannot resolve keyword 'XXXX' into field

This is a very strange error. I only receive it on my heroku server.
Here is how my model is:
# Abstract Model
class CommonInfo(models.Model):
active = models.BooleanField('Enabled?', default=False)
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Country(CommonInfo):
name = models.CharField('Country Name', db_index=True, max_length=200, help_text='e.g. France')
official_name = models.CharField('Official Name', max_length=400, blank=True, help_text='e.g. French Republic')
population = models.IntegerField('Population', help_text='Population must be entered as numbers with no commas or separators, e.g. 39456123', null=True, blank=True)
alpha2 = models.CharField('ISO ALPHA-2 Code', max_length=2, blank=True)
class News(CommonInfo):
title = models.CharField('Title', max_length=250)
slug = models.CharField('slug', max_length=255, unique=True)
body = models.TextField('Body', null=True, blank=True)
excerpt = models.TextField('Excerpt', null=True, blank=True)
author = models.ForeignKey(Author)
country = models.ManyToManyField(Country, null=True, blank=True)
def __unicode__(self):
return self.title
When I try to access News items from Admin site on my production server, I get this error (everything works fine on my dev server):
FieldError: Cannot resolve keyword 'news' into field. Choices are: active, alpha2, date_created, date_updated, id, name, official_name, population
File "/app/.heroku/python/lib/python2.7/site-packages/django/db/models/query.py", line 687, in _filter_or_exclude
clone.query.add_q(Q(*args, **kwargs))
File "/app/.heroku/python/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1271, in add_q
can_reuse=used_aliases, force_having=force_having)
File "/app/.heroku/python/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1139, in add_filter
process_extras=process_extras)
File "/app/.heroku/python/lib/python2.7/site-packages/django/db/models/sql/query.py", line 1337, in setup_joins
"Choices are: %s" % (name, ", ".join(names)))
I run the same django (1.5.4) and python (2.7.2) versions on my production and development environments.
My production server is Heroku
Any ideas what could triggers the error?
UPDATE:
admin.py config is as follow:
from django.contrib import admin
from APP.models import Country, News
class NewsForm(ModelForm):
class Meta:
model = News
class NewsAdmin(ModelAdmin):
form = NewsForm
search_fields = ['title',
'country__name']
list_filter = ('country',
'active'
)
list_per_page = 30
list_editable = ('active', )
list_display = ('title',
'active'
)
list_select_related = True
prepopulated_fields = {"slug": ("title",)}
admin.site.register(Country)
admin.site.register(News, NewsAdmin)
Finally, I was able to resolve the issue.
First, I managed to replicate the error in my local environment. At first, I was testing the application using built-in Django runserver. However, my production environment is Heroku that uses Gunicorn as webserver. When I switched to Gunicorn and foreman on my local server, I was able to replicate the error.
Second, I tried to pin point the issue by going through the models and add/remove different components, fields. To explain the process better, I have to add a missing piece to the original question.
The description I had posted above is kind of incomplete. I have another model in my models.py that I did not include in my original question, because I thought it was not relevant. Here is the complete model:
# Abstract Model
class CommonInfo(models.Model):
active = models.BooleanField('Enabled?', default=False)
date_created = models.DateTimeField(auto_now_add=True)
date_updated = models.DateTimeField(auto_now=True)
class Meta:
abstract = True
class Country(CommonInfo):
name = models.CharField('Country Name', db_index=True, max_length=200, help_text='e.g. France')
official_name = models.CharField('Official Name', max_length=400, blank=True, help_text='e.g. French Republic')
population = models.IntegerField('Population', help_text='Population must be entered as numbers with no commas or separators, e.g. 39456123', null=True, blank=True)
alpha2 = models.CharField('ISO ALPHA-2 Code', max_length=2, blank=True)
def get_country_names():
names = Country.objects.only('name').filter(active=1)
names = [(str(item), item) for item in names]
return names
class Person(CommonInfo):
name = models.CharField(max_length=200)
lastname = models.CharField(max_length=300)
country = models.CharField(max_length=250, choices=choices=get_country_names())
class News(CommonInfo):
title = models.CharField('Title', max_length=250)
slug = models.CharField('slug', max_length=255, unique=True)
body = models.TextField('Body', null=True, blank=True)
excerpt = models.TextField('Excerpt', null=True, blank=True)
author = models.ForeignKey(Author)
country = models.ManyToManyField(Country, null=True, blank=True)
def __unicode__(self):
return self.title
My model design didn't require a ForeignKey for Person's table, so I had decided to go with a simple CharField and instead, use a regular drop down menu. However, for some reason, Gunicorn raises the above mentioned error when, as part of the get_country_names(), the Country table is called before News. As soon as I deleted the get_country_names() and turned the country field on Person table into a regular CharField the issue was resolved.
Reading through the comments in this old Django bug and this post by Chase Seibert considerably helped me in this process.
Although ticket#1796 appears to be fixed more than 6 years ago, it seems that some tiny issues still remain deep buried there.
Thats it! Thanks everyone.
Adding to the possible situations under which this happens. I searched for the field that could not be found in any of my models.
Searching on the code I found that I was annotating a queryset with such field and then feeding that queryset as an __in search to another (along other complex queries).
My work around was to change that annotated queryset to return IDs and use that. On this particular case that result was always going to be small so the list of IDs was not a problem to pass.
I'd had some ManyToMany relationships that were working one-way. I had been messing around with my settings and changing the name of the main application a couple times. Somewhere along the lines, I had removed it from the INSTALLED_APPS section! Once I added that back in, then it worked. Definitely PEBKAC, but maybe this will help someone some day. It took me a while to think of checking for that, since the app was mostly working.
For example, my app is called deathvalleydogs. I had two models:
class Trip(ModelBase):
dogs = models.ManyToManyField(Dog, related_name="trips")
class Dog(ModelBase):
name = models.CharField(max_length=200)
when I tried to show a template for a Trip listing the Dogs that were on the trip like this:
{% for dog in trip.dogs.all %}
<li>{{ dog.name }}</li>
{% endfor %}
then I got an error of:
Cannot resolve keyword u'trips' into field. Choices are: active, birth_date, ...
Though I was still able to show a template for a Dog listing the trips they were on. Notice that trips should have been a field created by the m2m on the Dog objects. I wasn't referencing that field in the template, but it barfed on that field anyway in debug mode.
I wish the error had been more explicit, but I'm just so happy I finally found my mistake!!!
You can try to reset migrations:
Remove the all migrations files within your project.
Go through each of your projects apps migration folder (your_app/migrations/) and remove everything inside, except the init.py file.
Run makemigrations and migrate.
I was using the wrong dunder lookup on an admin model search field, so for example:
Not working:
class SomeAdmin(admin.ModelAdmin):
list_display = (
"id",
"thing_id",
"count",
"stuff_name",
"stuff_id"
)
readonly_fields = ("count")
# These cannot be resolved, because "stuff" doesn't exist on the model
search_fields = ("stuff__name", "stuff__id")
def stuff_name(self, obj):
return obj.thing.stuff.name
def stuff_id(self, obj):
return obj.thing.stuff.id
def get_queryset(self, request):
return super().get_queryset(request).select_related("thing")
Working:
class SomeAdmin(admin.ModelAdmin):
list_display = (
"id",
"thing_id",
"count",
"stuff_name",
"stuff_id"
)
readonly_fields = ("count")
search_fields = ("thing__stuff__name", "thing__stuff__id", "thing__id")
def stuff_name(self, obj):
return obj.thing.stuff.name
def stuff_id(self, obj):
return obj.thing.stuff.id
def get_queryset(self, request):
return super().get_queryset(request).select_related("thing")
I had this error. And if your are using POSTMAN to do an API call to a URL, then you may be encountering this same error.
django.core.exceptions.FieldError: Cannot resolve keyword 'player' into field. Choices are ...
Either in your model or serializer, you are referring to a particular field e.g. player in my case as you would when it is referenced as a foreign key.
In my case I had a Player model, and I wanted to update the reference in the save method of a Market model, when a weight is changed in the market, it should reflect immediately on the player.
class Market(models.Model):
player = models.ForeignKey(Player)
weight = models.CharField('Weight')
...
def save(self):
if self.weight:
# wrong
Player.objects.get(player=self.player) # this object reference of itself does not exist, hence the error
# right
Player.objects.get(pk=self.player.pk)
...
super().save()

Categories

Resources