Django auth. model when using multiple files - python

I have just broken up my models.py in to a module as follows:
models/
__init__.py
model1.py
model2.py
userModel.py
....
Where init.py imports all of the classes so I can still get at them using models.model1 as follows
from model1 import model1
from model2 import model2
from userModel import userModel
This is working ok however Django can no longer find the AUTH_USER_MODEL using:
AUTH_USER_MODEL = 'app.userModel'
I get the error:
LookupError: App 'app' doesn't have a 'userModel' model.
I have tried to change this to
AUTH_USER_MODEL = 'app.models.userModel'
but this doesn't work either. Any advice much appreciated
Jack

AUTH_USER_MODEL = 'app.UserModel'
UserModel is supposed to be a class that inherits from models.Model, not a module.
It will be passed through django.apps.apps.get_model(settings.AUTH_USER_MODEL) that takes a string app.Model where app is a django app registered in INSTALLED_APPS and Model is any model within app.models module and submodules.
Edit
Before Django 1.9, your models lying in a sub-module of app_name.models should define app_label = 'app_name' in their metaclass:
class MyUser(models.Model):
class Meta:
app_label = 'app_name'

Related

How can I enforce inheritance in my Django models?

In my Django app, I have an abstract model called MyModel that has e.g. created_at and updated_at fields. I want all the models in my project to subclass MyModel rather than using django.db.models.Model directly.
We have several developers on our app, so I want to use some sort of linter or CI check to enforce that this happens. How can I do this?
As mentioned by Willem Van Onsem in their comment you can write your own checks using the System check framework.
Assuming we have the following models in an app named "checktest" with Parent being the model that all models should inherit from:
from django.db import models
class Parent(models.Model):
class Meta:
abstract = True
# This should raise an error
class Foo(models.Model):
pass
# This should be fine
class Bar(Parent):
pass
We'll write a check as follows in a file checktest/custom_checks.py, note that the list APPS_TO_TEST contains the names of the apps whose models should inherit from the parent class:
from django.apps import apps
from django.core.checks import Error, register, Tags
from .models import Parent
# List of apps that should inherit from Parent
APPS_TO_TEST = ["checktest"]
#register(Tags.models)
def model_must_inherit(app_configs, **kwargs):
errors = []
for app in APPS_TO_TEST:
models = apps.get_app_config(app).get_models()
for model in models:
if not issubclass(model, Parent):
errors.append(Error(
f"Model {model.__name__} does not inherit from Parent",
hint="Models must inherit from the Parent class",
obj=model,
id="checktest.E001"
))
return errors
In the app configs ready method we'll import the above file so that the check will get run:
from django.apps import AppConfig
class ChecktestConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'checktest'
def ready(self) -> None:
from . import custom_checks
Now whenever we run commands like runserver or migrate the checks will get implicitly run. In a CI environment you can explicitly run the checks using the check command.

ForeignKey to 'self' in sub-application throws error on makemigrations in Django project

I'm currently working on an large Django project (version 1.10.7) and am running into an error with a model field in a sub-application. Here's what the basic structure looks like:
project/
app_one/
__init__.py
apps.py
models.py
urls.py
views.py
app_two/
__init__.py
apps.py
models.py
urls.py
views.py
The model and field in question looks like this (project/app_one/app_two/models.py):
class SampleModel(model.Model):
parent = models.ForeignKey('self', null=True, blank=True, related_name='members')
When I run python manage.py makemigrations app_one.app_two in the root folder I get this error message:
File .../django/db/models/utils.py", line 23, in make_model_tuple
"must be of the form 'app_label.ModelName'." % model ValueError: Invalid model reference 'app_one.app_two.SampleModel'. String model
references must be of the form 'app_label.ModelName'.
Here is code from other files that are relevant:
project/settings.py:
INSTALLED_APPS = filter(None, (
...
'app_one',
'app_one.app_two',
...
)
project/app_one/app_two/apps.py:
from __future__ import unicode_literals
from django.apps import AppConfig
class AppOneAppTwoConfig(AppConfig):
name = 'app_one.app_two'
label = 'app_one.app_two'
project/app_one/app_two/__init__.py:
default_app_config = 'app_one.app_two.apps.AppOneAppTwoConfig'
I believe the error here is that Django is only looking for one . in the full model name (app_one.app_two.SampleModel) to separate the app label from the model name in django/db/models/utils.py, and since there are two in this case, it fails.
My question is: This seems like a weird for Django not to account for...is there anyway to retain the dot notation of the app label and still have a self-referencing ForeignKey in a sub-application?
As you mention, it seems to be a lookup error when the project is trying to locate your app due to the nested apps. This can be solved by specifying the app name with an app_label in the models internal meta class:
class SampleModel(models.Model):
...
class Meta:
app_label = 'app_two'
I was able to solve this by changing the app_label to 'app_one_app_two' in apps.py. Because django references this when registering the related models, it doesn't break. All migrations are then registered under that label as well.

RemovedInDjango19Warning: Model doesn't declare an explicit app_label

Have gone through
Django 1.9 deprecation warnings app_label
but answers couldn't fix my problem, so asking again.
I have an app that is added to INSTALLED_APPS in settings.
when ever I run manage.py runserver, I get this warning,
[trimmed path to project]/catalog/models.py:9: RemovedInDjango19Warning: Model class catalog.models.Category doesn't declare an explicit app_label and either isn't in an application in INSTALLED_APPS or else was imported before its application was loaded. This will no longer be supported in Django 1.9.
class Category(models.Model):
The code from my app,
signals.py
from django.db.models.signals import post_save
from django.dispatch import receiver
from models import Category
#receiver(post_save, sender=Category)
def someSignal(sender, **kwargs):
pass
apps.py
from django.apps import AppConfig
class CatalogConfig(AppConfig):
name = 'catalog'
verbose_name = 'Catalogue'
init.py
import signals
default_app_config = 'catalog.apps.WhosConfig'
Django version 1.8.2 on Python 2.7.8
I experienced this issue when running tests and it was simply a matter of changing:
from .models import MyModel
to
from apps.myapp.models import MyModel
You are importing models.py before app configuration run.
To fix it, you could import and configure signals in CatalogConfig.ready method.
like this:
signals.py
def someSignal(sender, **kwargs):
pass
apps.py
from django.apps import AppConfig
from django.db.models.signals import post_save
class CatalogConfig(AppConfig):
name = 'catalog'
verbose_name = 'Catalogue'
def ready(self):
from .signals import someSignal
post_save.connect(
receiver=someSignal,
sender=self.get_model('Category')
)
you may want to check ready method in documentation

The model FlatPage is already registered

I'm attempting to work my way through Practical Django Projects. It seems to be a bit old, but I've manage to convert the code up to this point.
At this point the book would like me to change my models.py to be this:
class SearchKeyword(models.Model)
keyword = models.CharField(maxlength=50, core=True)
page = models.ForeignKey(FlatPage, edit_inline=models.STACKED,
min_num_in_admin=3, num_extra_on_change=1)
I know that this is now done in the admin.py instead. So my models.py looks like this:
from django.db import models
from django.contrib.flatpages.models import FlatPage
class SearchKeyword(models.Model):
keyword = models.CharField(max_length=50)
page = models.ForeignKey(FlatPage)
class Admin:
pass
def __unicode__(self):
return self.keyword
And the admin.py I've created now looks like this:
from search.models import SearchKeyword
from django.contrib import admin
from django.contrib.flatpages.models import FlatPage
class SearchKeywordInline(admin.StackedInline):
model = SearchKeyword
extra = 3
class FlatPageAdmin(admin.ModelAdmin):
model = FlatPage
inlines = [SearchKeywordInline]
admin.site.register(FlatPage, FlatPageAdmin)
When I load the Admin page, I receive:
AlreadyRegistered at /admin/
The model FlatPage is already registered
Exception Value:The model FlatPage is already registered
Thank you!
You have to unregister it first as the app itself ships with an admin.py
admin.site.unregister(FlatPage)
admin.site.register(FlatPage, FlatPageAdmin)

Split models.py into several files

I'm trying to split the models.py of my app into several files:
My first guess was do this:
myproject/
settings.py
manage.py
urls.py
__init__.py
app1/
views.py
__init__.py
models/
__init__.py
model1.py
model2.py
app2/
views.py
__init__.py
models/
__init__.py
model3.py
model4.py
This doesn't work, then i found this, but in this solution i still have a problem, when i run python manage.py sqlall app1 I got something like:
BEGIN;
CREATE TABLE "product_product" (
"id" serial NOT NULL PRIMARY KEY,
"store_id" integer NOT NULL
)
;
-- The following references should be added but depend on non-existent tables:
-- ALTER TABLE "product_product" ADD CONSTRAINT "store_id_refs_id_3e117eef" FOREIGN KEY ("store_id") REFERENCES "store_store" ("id") DEFERRABLE INITIALLY DEFERRED;
CREATE INDEX "product_product_store_id" ON "product_product" ("store_id");
COMMIT;
I'm not pretty sure about this, but i'm worried aboout the part The following references should be added but depend on non-existent tables:
This is my model1.py file:
from django.db import models
class Store(models.Model):
class Meta:
app_label = "store"
This is my model3.py file:
from django.db import models
from store.models import Store
class Product(models.Model):
store = models.ForeignKey(Store)
class Meta:
app_label = "product"
And apparently works but i got the comment in alter table and if I try this, same thing happens:
class Product(models.Model):
store = models.ForeignKey('store.Store')
class Meta:
app_label = "product"
So, should I run the alter for references manually? this may bring me problems with south?
For anyone on Django 1.9, it is now supported by the framework without defining the class meta data.
https://docs.djangoproject.com/en/1.9/topics/db/models/#organizing-models-in-a-package
NOTE: For Django 2, it's still the same
The manage.py startapp command creates an application structure that includes a models.py file. If you have many models, organizing them in separate files may be useful.
To do so, create a models package. Remove models.py and create a myapp/models/ directory with an __init__.py file and the files to store your models. You must import the models in the __init__.py file.
So, in your case, for a structure like
app1/
views.py
__init__.py
models/
__init__.py
model1.py
model2.py
app2/
views.py
__init__.py
models/
__init__.py
model3.py
model4.py
You only need to do
#myproject/app1/models/__init__.py:
from .model1 import Model1
from .model2 import Model2
#myproject/app2/models/__init__.py:
from .model3 import Model3
from .model4 import Model4
A note against importing all the classes:
Explicitly importing each model rather than using from .models import * has the advantages of not cluttering the namespace, making code more readable, and keeping code analysis tools useful.
I'd do the following:
myproject/
...
app1/
views.py
__init__.py
models.py
submodels/
__init__.py
model1.py
model2.py
app2/
views.py
__init__.py
models.py
submodels/
__init__.py
model3.py
model4.py
Then
#myproject/app1/models.py:
from submodels/model1.py import *
from submodels/model2.py import *
#myproject/app2/models.py:
from submodels/model3.py import *
from submodels/model4.py import *
But, if you don't have a good reason, put model1 and model2 directly in app1/models.py and model3 and model4 in app2/models.py
---second part---
This is app1/submodels/model1.py file:
from django.db import models
class Store(models.Model):
class Meta:
app_label = "store"
Thus correct your model3.py file:
from django.db import models
from app1.models import Store
class Product(models.Model):
store = models.ForeignKey(Store)
class Meta:
app_label = "product"
Edited, in case this comes up again for someone:
Check out django-schedule for an example of a project that does just this.
https://github.com/thauber/django-schedule/tree/master/schedule/models
https://github.com/thauber/django-schedule/
I've actually come across a tutorial for exactly what you're asking about, you can view it here:
https://web.archive.org/web/20190331105757/http://paltman.com/breaking-apart-models-in-django/
One key point that's probably relevant - you may want to use the db_table field on the Meta class to point the relocated classes back at their own table.
I can confirm this approach is working in Django 1.3
The relevant link for Django 3 is:
https://docs.djangoproject.com/en/3.2/topics/db/models/#organizing-models-in-a-package
Links to previous versions of documentation are broken. The example there is very succinct:
To do so, create a models package. Remove models.py and create a myapp/models/ directory with an init.py file and the files to store your models. You must import the models in the init.py file.
For example, if you had organic.py and synthetic.py in the models directory:
from .organic import Person
from .synthetic import Robot
Easiest Steps :
Create model folder in your app (Folder name should be model)
Delete model.py file from app directory (Backup the file while you delete it)
And after create init.py file in model folder
And after init.py file in write simple one line
And after create model file in your model folder and model file name should be same like as class name,if class name is 'Employee' than model file name should be like 'employee.py'
And after in model file define your database table same as write like in model.py file
Save it
My Code : from django_adminlte.models.employee import Employee
For your : from app_name.models.model_file_name_only import Class_Name_which_define_in_model_file
__init__.py
from django_adminlte.models.employee import Employee
model/employee.py (employee is separate model file)
from django.db import models
class Employee(models.Model):
eid = models.CharField(max_length=20)
ename = models.CharField(max_length=20)
eemail = models.EmailField()
econtact = models.CharField(max_length=15)
class Meta:
db_table = "employee"
# app_label = 'django_adminlte'
def __str__(self):
return self.ename
I wrote a script that might be useful.
github.com/victorqribeiro/splitDjangoModels
it split the models in individual files with proper naming and importing; it also create an init file so you can import all your models at once.
let me know if this helps

Categories

Resources