Split models.py into several files - python

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

Related

Django, makemigrations doesn't detect model changes

I am learning Django and I am using this tutorial:
https://www.youtube.com/watch?v=F5mRW0jo-U4&t=912s
I created an app called products and I have it in INSTALLED_APPS.
Upon creating a Product class in the models.py file in the products app folder, I created some fields such as price.
However, when I tried to use the makemigrations command, no changes detected was reported.
Folder Layout and Settings
models.py
from django.db import models
# Create your models here.
class Product(models.Model):
title = models.TextField()
description = models.TextField()
price = models.TextField()
I checked if there were any issues with the Product class but there doesn't seem to be any as far as I can see, so I am at a loss as to why no changes were detected.
15
You may have deleted the migrations folder inside the app or init.py inside the /migrations/ folder, create a new one
myproject/ apps/
myapp/
migrations/
init.py
You can always do makemigrations seperately without doing the above step
python manage.py makemigrations myapp
Refer the table - djangomigrations .. if the entry is there for the makemigration file already in the table then trunckage and rerun makemigrations and then do migrate

Cant import models.py in views.py

I want to use my model file from my database by importing it in views.py. But I get an error when I call a class from models.py.
ERROR: ImportError: cannot import name Vehicle
My path:
+ vehicle
---> __init__.py
---> models.py
---> views.py
vehicle/models.py
from __future__ import unicode_literals
from django.db import models
class Vehicle(models.Model):
id = models.BigAutoField(primary_key=True)
plate = models.TextField()
driver = models.ForeignKey(Driver, models.DO_NOTHING)
class Meta:
managed = False
db_table = 'vehicle'
Vehicle/views.py
from vehicle.models import Vehicle
s = Vehicle.objects.all()
print s
If you are calling model in view, you can call it directly
from model import Vehicles
If you want to have a full path, you can add the location of vehicles on your Path
import sys
sys.path.insert(0, 'path_to_vehicle_folder')
from vehicles.model import Vehicles
The way you are doing now, assumes that your execution happens outside vehicles directlory
vehicles\
__init__.py
model.py
view.py
this.py
In this.py you can import as you did.
You can always execute print(sys.path), to see where your python is going to look for packages/modules :) with the sys.path.insert, we add a location we want to include when looking for modules. There exists better ways :)

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.

Django auth. model when using multiple files

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'

Django: Foreign key from parent to child table

I want to have a foreign key link from Parent table to child table ; for a site which is mainly backend powered. Currently only admin part of site is active as that is the only part required to get our information team working.
App structure:
attraction:
|
|--- model
| |
| | --- _ _init__.py
| | --- imagemetadata.py
|
| --- models.py
| --- admin.py
(imagemetadata is under directory model)
file imagemetadata.py
from attraction.models import Attraction
class ImageMetadata(models.Model):
image = models.ImageField(upload_to='', blank=True, null=True)
upload_time = models.DateTimeField(null=True)
attraction = models.ForeignKey(Attraction)
file models.py
from django.db import models
class Attraction(models.Model):
name = models.CharField(max_length=100, null=False)
description = models.CharField(max_length=500, null=True)
url = models.URLField(max_length=200, null=True)
file admin.py
class ImageMetadataInline(admin.TabularInline):
model = ImageMetadata
extra = 2
class AttractionAdmin(admin.ModelAdmin):
fieldsets = [
(None, {'fields': ['name', 'description', 'url']}),
]
inlines = [ImageMetadataInline, PriceInline]
admin.site.register(Attraction, AttractionAdmin)
The problem i am facing is i cant get a foreign key from Attraction to Imagemetadata. If I try to make a two way foreign key import error occurs. ( Cyclic imports not allowed in Python).
And because of the structure of admin , there cant be a foreign key just from Attraction to ImageMetadata, as Django doesnt allow that.
Is there a way to make the foreign key point from Attraction to ImageMetadata, without changing the structure of admin ?
You can define the class Meta as following:
class ImageMetadata(models.Model):
# ... your fields
class Meta:
app_label = 'attraction'
Make a sub-directory models in your app directory. Split your models into different files. Then edit the __init__.py:
from model_file1 import *
from model_fiel2 import *
# and so on
Now you can make:
from attraction.models import whatever
I split in this way my tests. It should work for the models too.
However I would suggest you first to rethink your design. In Python is absolutely all right to have many classes in one file. Python is neither PHP nor Java. 200 LOC per file is nothing. I would say it is absolutely okay if your models.py are up to 2k LOC. Just my personal opinion. Maybe some Python gurus here can correct me.
Before you split your models.py consider if you can maybe split your project in smaller apps. Maybe ImageMetadata should belong to another app. You can't achieve granularity by splitting the models.py into many files. Just because you have many smaller files instead of one big file doesn't mean the project logic is split. You should break down your logic into few self-containing apps.

Categories

Resources