Importing from views to models in Django - python

I have something like this in one of my apps's models.py :
class Account(AbstractBaseUser):
email = models.EmailField(unique=True)
I want to import a class from another app's views.py as below:
from anotherapp.views import MyClass
The problem is in the first lines of anotherapp.views file, I imported the Account class. So when I want to import MyClass into my models.py file, this error raises :
ImportError: cannot import name Account

That is circular import error you are encountering. While it is bad practice to import from views to models in Django, if you still want to, you can follow methods in this question to resolve it.
Here are few alternative ways that can be considered as good practice:
You can consider importing other low-level modules exist within anotherapp instead of MyClass (not depending on views.py)
You can use Django's signals to catch anotherapp's events project wide and act according to.
You can create a third file, say utils.py in anotherapp, move MyClass there and let anotherapp/views.py and your app's models.py import from anotherapp.utils

Related

How to import all the classes in a module as a list?

In django, to display models on admin page, we have to first register all the models in admin.py.
If there are so many models to register, we have to write admin.site.register(model_name) so many times.
Its kind of a boring task.
So I was wondering if there is any way in python to import all the classes in module as list.
for instance, admin.py,
from django.contrib import admin
from .models import * as list_name #like this is there any way we can get all things inside * in list ?
for i in list_name:
admin.site.register(i) #so that this way I have to write only one line of code to register all classes.
Is there any way to save writing lot of lines here ?
I think that the dir function will be useful here. Something like:
from django.contrib import admin
from django.db.models import Model
import models
for i in dir(models):
m = getattr(models, i)
if isinstance(m, Model):
admin.site.register(m)
First of all, site.register allow you to pass a model or iterable of models (docs) - so you can simply pass a list of models you want to register. To accomplish this automatically - without ever needing to edit the admin.py module - I suggest to reimport your models in an __init__.py file.
from my_app import models
site.register(map(models.__dict__.get, models.__all__))
Content of my_app.models.__init__.py:
from .cat import Cat
from .dog import Dog
__all__ = ["Cat", "Dog"]

How to create a package of models in Django

Having a quite large models.py file (which contains several models), I'm trying to refactor, with one model per file.
I'm therefore trying to create a models package, with the following structure:
app/models/__init__.py
app/models/first_model.py
app/models/second_model.py
Unfortunately, I cannot get Django lazy reference mechanism to work well, i.e.:
first_model = models.ForeignKey('app.FirstModel')
returns the error that Django cannot find the model.
Any idea? Thanks!
It should work, make sure that in __init__.py you are importing all the models from first_model.py and second_model.py.
from .first_model import FirstModel
from .second_model import SecondModel
EDIT: If you want to retrieve the models as 'app_label.model_name', then you will have to import them in __init__.py, otherwise you can try the following:
Use https://docs.djangoproject.com/en/2.0/ref/applications/#django.apps.apps.get_model
Or you can use ContentTypes: https://docs.djangoproject.com/en/2.0/ref/contrib/contenttypes/#methods-on-contenttype-instances
The presence of __init__.py tells Python to consider it as a Package, but in order to let Django find your models for Migration and find it well, you should import all your models stuff in __init__.py
Keep the structure like it was:
app/models/__init__.py
app/models/first_model.py
app/models/second_model.py
__init__.py
from .first_models import *
from .second_models import *
In the Cookbook there is an example using only the name of the class (model) and not the app part:
first_model = models.ForeignKey('FirstModel')

Django 1.9 deprecation warning when importing models in __init__.py

I have shortcut function declared in __init__.py of my module in order to simplify importing it, ie.
from app.logger import log
instead of:
from app.logger.shortcuts import log
This function is a class object in principle, intendet to be used like singleton in this way:
# __init_.py
from app.logger.backends import LogDatabaseBackend
log = LogDatabaseBackend()
In the backends.py i need to import some models in this way:
# backends.py
from app.logger.models import Model1, Model2
class LogDatabaseBackend(object):
...
These models are necessary in some methods of LogDatabaseBackend. The problem is that Django show warnings like:
"RemovedInDjango19Warning: Model class app.logger.models.Model1 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."
I think the reason is importing model in init, before app was loaded. How should I change my code to avoid these warnings and maintain Django 1.9 compatibility?
Define application configuration in <app_name>.apps.py :
From Application Configuration documentation :
Application configuration objects store metadata for an application.
Some attributes can be configured in AppConfig subclasses. Others are
set by Django and read-only.
Here is an example from the documentation :
# rock_n_roll/apps.py
from django.apps import AppConfig
class RockNRollConfig(AppConfig):
name = 'rock_n_roll'
verbose_name = "Rock ’n’ roll"
You can make your application load this AppConfig subclass by default as
follows:
# rock_n_roll/__init__.py
default_app_config = 'rock_n_roll.apps.RockNRollConfig'
That will cause RockNRollConfig to be used when INSTALLED_APPS just
contains 'rock_n_roll'. This allows you to make use of AppConfig
features without requiring your users to update their INSTALLED_APPS
setting. Besides this use case, it’s best to avoid using
default_app_config and instead specify the app config class in
INSTALLED_APPS as described next.
If Model1 and Model2 are used with ForeignKey, you don't need to import them. Instead use ForeignKey("app.Model1", ...). This delays the import till after app registry. Otherwise, consider using function-level import.

Django: Refactoring models into sub-modules

After the models.py in one app grew quite large, I've tried to move some of the classes into subpackages.
The old structure was something like this:
# File: assets/models.py
class Asset(...):
# lots of irrelevant code
# File: widgets/models.py
from assets.models import Asset
class Video(...):
asset = models.ForeignKey(Asset)
This worked without a problem, so I'm not going into further details about the structure.
What I've tried to do now is move the Asset class into a submodule. The structure is now as follows:
# File: assets/models/__init__.py (of course I deleted the old models.py)
from .assets import Asset
# File: assets/models/assets.py
class Asset(...):
# lots of irrelevant code
# File: widgets/models.py
from assets.models.assets import Asset
class Video(...):
asset = models.ForeignKey(Asset)
Somehow this doesn't work, and I can't figure out what actually causes the trouble. The error I'm getting is this:
widgets.video: 'asset' has a relation with model , which has either not been installed or
is abstract
It appears that Django can't reliably detect which app a model belongs to if it's in a nested submodule (ie. not directly inside APPNAME.models).
This is a known problem and can be solved by adding the following lines (in this case to the Asset class), thus defining explicitly which app a model belongs to:
class Asset(models.Model):
...
class Meta:
app_label = 'assets'
...
References:
https://groups.google.com/forum/#!topic/django-users/MmaiKvbDlDc
https://code.djangoproject.com/ticket/14007
You should import from models as before:
from assets.models import Asset
This allows you to always import from models but organise the models separately within the models directory. It also means, conceptually, that Asset is still in models as your ForeignKey refers to a assets.models.Asset object, not assets.models.assets.Asset.

Django: Is separating views.py into its own module a good idea?

Dilemma
My views.py gets pretty unwieldy, so I want to separate it into a separate views module inside of my app. However, I'm not sure this is a good idea, for two reasons:
If my views file is the same name as the app name, I cannot import the model without using django.db.get_model, therefore I am worried my approach may be flawed. I have heard it is best practice to avoid name collision within modules; should I rename my view files?
I'm not sure if creating a views module is considered good practice within the Django community in general.
Example
For example, for an app named blogs, with a Blog model and a Post model:
blogs/
__init__.py
models.py
urls.py
views/
__init__.py
blogs.py
posts.py
Here is my blogs.views.blogs:
# project/blogs/views/blogs.py
from django.db.models import get_model
from django.shortcuts import get_object_or_404
from django.views.generic import ListView, DetailView
# Cannot import model directly, results in `ImportError: No module named models`.
# This can be resolved if I resolve the name collision between this file and
# the app itself.
#
# from blogs.models import Blog
class BlogListView(ListView):
model = get_model('blogs', 'Blog')
def get_queryset(self):
return self.model.objects.all()
class BlogDetailView(DetailView):
model = get_model('blogs', 'Blog')
def get_object(self):
blog_pk = self.kwargs.get('blog_pk')
return get_object_or_404(self.model.objects, pk=blog_pk)
Question
My question is twofold:
Should I be separating my views in the first place?
If so, is using get_model a good idea, or is there a way I can import my model directly without using this method? Or should I change my view file names, for example by adding the suffix _views (e.g.: blogs.views.blogs_views.py) in order to avoid the problem altogether?
I cannot import the model without using django.db.get_model
You can: from project_name.app_name.models import MyModel And it's preferable way, 'relative imports for intra-package imports are highly discouraged', - as said in PEP-8.
There shouldn't be any problems with names, views.py has no special meaning in Django, it's just a convention.
You can keep your views in any file in any module under any name you want. So there is no special rules here, if you think that separating the module into submodules will be good, do it.
As DrTyrsa points out, views has no special meaning. So as an alternative to creating a subpackage, you could just create several files at the same level as the existing views.py - blog_views.py, posts_views.py, etc. As long as you use the correct reference in urls.py, this works fine.

Categories