Django User object error in models.py - python

I am writing my first Django project and using Django 1.7, and for my login and authentication I am using the Django User model. I am now creating my models. Project and User share a Many-to-Many relationship
models.py:
from django.db import models
from django import forms
from django.contrib.auth.models import User
class Project(models.Model):
project_name = models.CharField(max_length=128, unique = True)
project_description = models.CharField(max_length=128)
users_annotating = models.ManyToManyField(User)
However, I get this error when I try to migrate:
ValueError: Related model 'auth.User' cannot be resolved
Does anyone understand this problem?

I'm guessing that you have
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
in your newly created Django 1.7 migration.
If you comment out that dependency and just in case replace it with
dependencies = [
('auth', '__first__'),
]
things should work.

Related

Migrations applied in site-packages.django.contrib.sites instead of local app after extending Site object with OneToOne field

I borrowed ideas from this answer to extend the default Site object created during the initial migration with Django sites framework. The new model, SiteSettings, establishes a OneToOne relationship with the Site model to add additional fields. I then use signals to create the SiteSettings object.
When I made the first migration for the SiteSettings model everything appeared to work fine. A SiteSettings object was created that had a OneToOne relationship with the default Site object.
However, what I didn't notice is that a migration file wasn't created under my local app for this. I was able to makemigrations and migrate just fine, so I'm not sure where that migration went. It's not listed in my migrations table.
Anyway, since it worked I didn't notice. I then proceeded to add additional fields to SiteSettings a day or two later, and noticed when I made those migrations, they were for creating a SiteSettings model, not updating its fields. That's when I noticed that the first migration wasn't created in the right spot. The second migration was created, however it was created in site-packages/django/contrib/sites/migrations/. It looks like this:
class Migration(migrations.Migration):
dependencies = [
("sites", "0002_alter_domain_unique"), # the initial auto migration
]
operations = [
migrations.CreateModel(
name="SiteSettings",
fields=[
(
"site",
models.OneToOneField(
on_delete=django.db.models.deletion.CASCADE,
primary_key=True,
related_name="settings",
serialize=False,
to="sites.site",
verbose_name="site",
),
),
# A bunch of other CharFields that aren't important
],
options={
"verbose_name_plural": "settings",
},
),
]
And my models.py looks like this. I'm assuming the issue may be with the app_label matching the name of django.contrib.sites, but I'm not sure. The reason I named the label that was so it shows up under sites in the admin.
class SiteSettings(models.Model):
"""
Extension of the Sites model that holds more info about the site.
"""
site = models.OneToOneField(
Site,
on_delete=models.CASCADE,
primary_key=True,
related_name="settings",
verbose_name="site",
)
# A bunch of other fields that aren't important
def __str__(self):
return self.site.name
class Meta:
app_label = "sites"
verbose_name_plural = "settings"
Here's what the apps.py looks like:
from django.apps import AppConfig
from django.conf import settings
from django.db.models.signals import post_migrate, post_save
def create_default_site_settings(sender, **kwargs):
"""Creates default site settings after migration"""
# App config must be ready for import to work
from django.contrib.sites.models import Site
from .models import SiteSettings
site = Site.objects.get(id=getattr(settings, "SITE_ID", 1))
if not SiteSettings.objects.exists():
SiteSettings.objects.create(site=site)
class CoreConfig(AppConfig):
name = "apps.core"
label = "core"
default_auto_field = "django.db.models.BigAutoField"
def ready(self):
# App config must be ready for import to work
from django.contrib.sites.models import Site
post_migrate.connect(create_default_site_settings, sender=self)
from .signals import create_site_settings
post_save.connect(create_site_settings, sender=Site)
And lastly, signals.py.
from django.contrib.sites.models import Site
from django.db.models.signals import post_save
from django.dispatch import receiver
from .models import SiteSettings
#receiver(post_save, sender=Site)
def create_site_settings(sender, instance, **kwargs):
"""
Creates/updates a SiteSettings object after a Site object.
"""
site_settings, created = SiteSettings.objects.update_or_create(site=instance)
if not created:
site_settings.save()
Is this simply an issue with the app_label being the same? I'm trying to wrap my head around why that is if so.
I figured out the issue. Since I was running my app in a Docker container and had the app_label in my new model set to the same app_label as django.contrib.sites, the migrations were being created in path/to/site-packages/django/contrib/sites/migrations/ in the Docker container. Not in the app copied to the container, but the actual Django install inside the container.
So that initial migration was created in the container and not locally which I didn't notice at first. Once my container was rebuilt at some point, the migration file was wiped since it freshly reinstalls all Python packages onto the new image. When I went to make the second migration, it was like the model was never created despite the table existing in the db.
To fix it, I reverted the app_label of the new model back to its default. Then I had to I had to rebuild the container, delete the old table site_sitesettings and remove the "lost" migration record in django_migrations table. During the next round of migrations a new table was created for core_sitesettings and I could access my OneToOne relationships as expected.

Django: no such table: users_profile

Django noob here. Whenever I extend the base User Model, and try to access or edit the new fields, I receive the following error [... table not found].
OperationalError at /admin/users/profile/
no such table: users_profile
Request Method: GET
Request URL: http://127.0.0.1:8000/admin/users/profile/
Django Version: 3.1.2
Exception Type: OperationalError
Exception Value:
no such table: users_profile
Exception Location: /Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages/django/db/backends/sqlite3/base.py, line 413, in execute
Python Executable: /Library/Frameworks/Python.framework/Versions/3.7/bin/python3
Python Version: 3.7.7
Python Path:
['/Users/rexmarkman/Desktop/ComputerScienceIA/newproject',
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python37.zip',
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7',
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/lib-dynload',
'/Users/rexmarkman/Library/Python/3.7/lib/python/site-packages',
'/Library/Frameworks/Python.framework/Versions/3.7/lib/python3.7/site-packages']
Server time: Sun, 30 May 2021 12:52:22 +0000
Im not sure why this is happening as I have ran makemigrations and migrate multiple times. For reference here are my migrations
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Profile',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('profile_picture', models.ImageField(default='default.jpg', upload_to='profile_pictures')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
]
Also here is my models.py for further reference
from django.db import models
from django.contrib.auth.models import User
from django.forms import ModelForm
class Profile(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
profile_picture = models.ImageField(default = 'default.jpg', upload_to = 'profile_pictures')
def __str__(self):
return f'{self.user.username} Profile'
Also worth noting that this issue occurs when attempting to change the profile table in the admin site
Please let me know if you need to know anything else.
Thanks for any help :)
Try to delete the migrations folder from your app and db.sqilte3 file and then retry -:
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser
and then retry your error must be solved

How can I handle the "Django - Database error: no such table" error?

I'm pretty new to django and I'm stuck at the problem with models. Here is my users app's models file:
from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MinValueValidator, MaxValueValidator
from django.urls import reverse
from PIL import Image
class Schedule(models.Model):
title = models.CharField(max_length=150)
context = models.TextField()
class Input(models.Model):
DAYS_OF_FITNESS = [
('month', '30'),
('month_2', '60'),
('month_3', '90'),
]
weight = models.PositiveIntegerField(validators=[MinValueValidator(40)])
age = models.PositiveIntegerField(validators=[MinValueValidator(12)])
days = models.CharField(
max_length=20, choices=DAYS_OF_FITNESS, default='30')
athlete = models.OneToOneField(User, primary_key=True, on_delete=models.CASCADE)
schedule = models.ForeignKey(Schedule, on_delete=models.CASCADE)
I'm trying to link each user with its input using OneToOneField and Schedule is linked as a foreign key to Input model
But when I'm trying to migrate models, I'm getting this error:
return Database.Cursor.execute(self, query, params)
django.db.utils.OperationalError: no such table: users_input
I checked that Both models are in django settings, and I migrated them. BTW, I'm using signals to save inputs automatically, (just for note if it helps)
It's because that table doesn't exist in the database. To create tables you have to do the following. Execute the following commands in your terminal.
python manage.py makemigrations
python manage.py migrate

Correct way to extend AbstractUser in Django?

I'm trying to integrate two django apps where each had their individual auths working. To do that, I'm trying to subclass AbstractUser instead of User. I'm following the PyBB docs and Django#substituting_custom_model. I've removed all migration files in all my apps apart from their individual init.py (including the migrations from the PyBB library sitting in my site-packages). I've also changed the Mysql database to a blank one to start afresh and I'm trying to subclass AbstractUser as shown below.
My Models.py:
from django.contrib.auth.models import User
from django.contrib.auth.models import AbstractUser
from django.db import models
class Student_User(models.Model):
"""
Table to store accounts
"""
su_student = models.OneToOneField(AbstractUser)
USERNAME_FIELD = 'su_student'
su_type = models.PositiveSmallIntegerField(db_column='su_type', default=0)
su_access = models.TextField(db_column='su_access', default='')
su_packs = models.TextField(db_column='su_packs', default='')
REQUIRED_FIELDS = []
def __unicode__(self):
return str(self.su_student)
My settings.py:
AUTH_USER_MODEL = "app.Student_User"
PYBB_PROFILE_RELATED_NAME = 'pybb_profile'
When running makemigrations for my primary app, I get this error:
app.Student_User.su_student: (fields.E300) Field defines a relation with model 'AbstractUser', which is either not installed, or is abstract.
How do I achieve what I am trying to do here?
PS: The app was working fine with onetoone with User without username_field or required_field.
PPS: I just checked the AbstractUser model in my contrib.auth.models and it has class Meta: abstract = True. Ok so its abstract, still, how do I resolve this? I just need one login, currently, two parts of my site, although connected through urls, ask for seperate logins and dont detect each others logins. What do I need to do for this?
You can't have a one-to-one relationship with an abstract model; by definition, an abstract model is never actually instantiated.
AbstractUser is supposed to be inherited. Your structure should be:
class Student_User(AbstractUser):
...

How do you extend the Site model in django?

What is the best approach to extending the Site model in django? Creating a new model and ForeignKey the Site or there another approach that allows me to subclass the Site model?
I prefer subclassing, because relationally I'm more comfortable, but I'm concerned for the impact it will have with the built-in Admin.
I just used my own subclass of Site and created a custom admin for it.
Basically, when you subclass a model in django it creates FK pointing to parent model and allows to access parent model's fields transparently- the same way you'd access parent class attributes in pyhon.
Built in admin won't suffer in any way, but you'll have to un-register Sites ModelAdmin and register your own ModelAdmin.
If you only want to change behaviour of the object, but not add any new fields, you should consider using a "proxy model" (new in Django 1.1). You can add extra Python methods to existing models, and more:
This is what proxy model inheritance is for: creating a proxy for the original model. You can create, delete and update instances of the proxy model and all the data will be saved as if you were using the original (non-proxied) model. The difference is that you can change things like the default model ordering or the default manager in the proxy, without having to alter the original.
Read more in the documentation.
As of Django 2.2 there still no simple straight way to extend Site as can be done for User. Best way to do it now is to create new entity and put parameters there. This is the only way if you want to leverage existing sites support.
class SiteProfile(models.Model):
title = models.TextField()
site = models.OneToOneField(Site, on_delete=models.CASCADE)
You will have to create admin for SiteProfile. Then add some SiteProfile records with linked Site. Now you can use site.siteprofile.title anywhere where you have access to current site from model.
You can have another model like SiteProfile which has a OneToOne relation with Site.
It has been a long time since the question was asked, but I think there is not yet (Django 3.1) an easy solution for it like creating a custom user model. In this case, creating a custom user model inheriting from django.contrib.auth.models.AbstractUser model and changing AUTH_USER_MODEL (in settings) to the newly created custom user model solves the issue.
However, it can be achieved for also Site model with a long solution written below:
SOLUTION
Suppose that you have an app with the name core. Use that app for all of the code below, except the settings file.
Create a SiteProfile model with a site field having an OneToOne relation with the Site model. I have also changed its app_label meta so it will be seen under the Sites app in the admin.
# in core.models
...
from django.contrib.sites.models import Site
from django.db import models
class SiteProfile(models.Model):
"""SiteProfile model is OneToOne related to Site model."""
site = models.OneToOneField(
Site, on_delete=models.CASCADE, primary_key=True,
related_name='profiles', verbose_name='site')
long_name = models.CharField(
max_length=255, blank=True, null=True)
meta_name = models.CharField(
max_length=255, blank=True, null=True)
def __str__(self):
return self.site.name
class Meta:
app_label = 'sites' # make it under sites app (in admin)
...
Register the model in the admin. (in core.admin)
What we did until now was good enough if you just want to create a site profile model. However, you will want the first profile to be created just after migration. Because the first site is created, but not the first profile related to it. If you don't want to create it by hand, you need the 3rd step.
Write below code in core.apps.py:
# in core.apps
...
from django.conf import settings
from django.db.models.signals import post_migrate
def create_default_site_profile(sender, **kwargs):
"""after migrations"""
from django.contrib.sites.models import Site
from core.models import SiteProfile
site = Site.objects.get(id=getattr(settings, 'SITE_ID', 1))
if not SiteProfile.objects.exists():
SiteProfile.objects.create(site=site)
class CoreConfig(AppConfig):
name = 'core'
def ready(self):
post_migrate.connect(create_default_site_profile, sender=self)
from .signals import (create_site_profile) # now create the second signal
The function (create_default_site_profile) will automatically create the first profile related to the first site after migration, using the post_migrate signal. However, you will need another signal (post_save), the last row of the above code.
If you do this step, your SiteProfile model will have a full connection with the Site model. A SiteProfile object is automatically created/updated when any Site object is created/updated. The signal is called from apps.py with the last row.
# in core.signals
from django.contrib.sites.models import Site
from django.db.models.signals import post_save, post_migrate
from django.dispatch import receiver
from .models import SiteProfile
#receiver(post_save, sender=Site)
def create_site_profile(sender, instance, **kwargs):
"""This signal creates/updates a SiteProfile object
after creating/updating a Site object.
"""
siteprofile, created = SiteProfile.objects.update_or_create(
site=instance
)
if not created:
siteprofile.save()
Would you like to use it on templates? e.g.
{{ site.name }}
Then you need the 5th and 6th steps.
Add the below code in settings.py > TEMPLATES > OPTIONS > context_processors
'core.context_processors.site_processor'
# in settings.py
TEMPLATES = [
{
# ...
'OPTIONS': {
'context_processors': [
# ...
# custom processor for getting the current site
'core.context_processors.site_processor',
],
},
},
]
Create a context_processors.py file in the core app with the code below.
A try-catch block is needed (catch part) to make it safer. If you delete all sites from the database you will have an error both in admin and on the front end pages. Error is Site matching query does not exist. So the catch block creates one if it is empty.
This solution may not be fully qualified if you have a second site and it is deleted. This solution only creates a site with id=1.
# in core.context_processors
from django.conf import settings
from django.contrib.sites.models import Site
def site_processor(request):
try:
return {
'site': Site.objects.get_current()
}
except:
Site.objects.create(
id=getattr(settings, 'SITE_ID', 1),
domain='example.com', name='example.com')
You can now use the site name, domain, meta_name, long_name, or any field you added, in your templates.
# e.g.
{{ site.name }}
{{ site.profiles.long_name }}
It normally adds two DB queries, one for File.objects and one for FileProfile.objects. However, as it is mentioned in the docs,
Django is clever enough to cache the current site at the first request and it serves the cached data at the subsequent calls.
https://docs.djangoproject.com/en/3.1/ref/contrib/sites/#caching-the-current-site-object
Apparently, you can also create a models.py file in a folder that you add to INSTALLED_APPS, with the following content:
from django.contrib.sites.models import Site as DjangoSite, SiteManager
from django.core.exceptions import ImproperlyConfigured
from django.db import models
from django.http.request import split_domain_port
# our site model
class Site(DjangoSite):
settings = models.JSONField(blank=True, default={})
port = models.PositiveIntegerField(null=True)
protocol = models.CharField(default='http', max_length=5)
#property
def url(self):
if self.port:
host = f'{self.domain}:{self.port}'
else:
host = self.domain
return f'{self.protocol}://{host}/'
# patch django.contrib.sites.models.Site.objects to use our Site class
DjangoSite.objects.model = Site
# optionnal: override get_current to auto create site instances
old_get_current = SiteManager.get_current
def get_current(self, request=None):
try:
return old_get_current(self, request)
except (ImproperlyConfigured, Site.DoesNotExist):
if not request:
return Site(domain='localhost', name='localhost')
host = request.get_host()
domain, port = split_domain_port(host)
Site.objects.create(
name=domain.capitalize(),
domain=host,
port=port,
protocol=request.META['wsgi.url_scheme'],
)
return old_get_current(self, request)
SiteManager.get_current = get_current
In my opinion, the best way to doing this is by writing a model related to the site model using inheritance
First, add the site id to the Django settings file
SITE_ID = 1
now create a model in one of your apps
from django.db import models
from django.contrib.sites.models import Site
class Settings(Site):
field_a = models.CharField(max_length=150, null=True)
field_b = models.CharField(max_length=150, null=True)
class Meta:
verbose_name_plural = 'settings'
db_table = 'core_settings' # core is name of my app
def __str__(self) -> str:
return 'Settings'
then edit the apps.py file of that app
from django.apps import AppConfig
from django.db.models.signals import post_migrate
def build_settings(sender, **kwargs):
from django.contrib.sites.models import Site
from .models import Settings
if Settings.objects.count() < 1:
Settings.objects.create(site_ptr=Site.objects.first())
class CoreConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'project.apps.core'
def ready(self) -> None:
post_migrate.connect(build_settings, sender=self)
now every time you run migrations a row will be auto-generated in core_settings that have a one to one relationship with your Site model
and now you can access your settings like this
Site.objects.get_current().settings.access_id
optional: if have only one site
unregister site model from admin site and disable deleting and creating settings model in admin site
from django.contrib import admin
from . import models
from django.contrib.sites.models import Site
admin.site.unregister(Site)
#admin.register(models.Settings)
class SettingAdminModel(admin.ModelAdmin):
def has_delete_permission(self, request,obj=None) -> bool:
return False
def has_add_permission(self, request) -> bool:
return False

Categories

Resources