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.
Related
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"]
Info
I have two simple models in Django, with one-to-one relationship
I'm using generic views
There are Issues and Solutionss in the database, numbered 1 through 10
Loading the Issue via the DetailView (e.g. localhost:8000/myapp/6/) works great
Error
When trying to load the Solution view in a browser (e.g. localhost:8000/myapp/6/solution/), I get Page not found (404), No solution found matching the query.
Code
models.py:
class Issue(models.Model):
def __str__(self):
return self.issue_text
issue_text = models.CharField(max_length=200)
class Solution(models.Model):
def __str__(self):
return self.solution_text
issue = models.OneToOneField(Issue, on_delete=models.CASCADE)
solution_text = models.CharField(max_length=200)
views.py:
class DetailView(generic.DetailView):
model = Issue
template_name = 'my_templates/detail.html'
class SolutionView(generic.DetailView):
model = Solution
template_name = 'my_templates/solution.html'
urls.py:
urlpatterns = [
url(r'^(?P<pk>[0-9]+)/$', views.DetailView.as_view(), name='detail'),
url(r'^(?P<pk>[0-9]+)/solution/$', views.SolutionView.as_view(), name='solution'),
]
Question
I suspect that maybe the relationship between the models is incorrect - I can see that the view raises the 404 error because it can't find a solution object (though there are a few solution objects in the database, for every Issue).
I've been going through Django's docs on generic views and making Django queries to the database but I think I'm confusing the two.
Also, debugging with pdb just makes the browser lose the object for some reason.
Did I get the one-to-one relationship wrong?
Which Django Version did you use? try this...
urls.py
urlpatterns = [
path('solution/<int:pk>/', SolutionView.as_view(), name='solution'),
]
views.py
class SolutionView(DetailView):
model = Solution
template_name ="my_templates/solution.html"
def get_object(self):
some_pk = self.kwargs.get("pk")
return get_object_or_404(Solution, pk=some_pk)
Tested. This works for me fine. It´s django 3.0 but i guess down to version 2.x this should also work.
you have to send the integer variable from your request to your class based view some_pk so that django can get the right object.
page not found relates also to your template path - so check it.
Don´t forget to set the right template_name and to import everything.
There was a mismatch between the app name and the object instance.
In this case, the app, the module and the model objects were named differently. For example - following django's tutorial: the name of the app should be polls and the templates sub-directory should also be polls/templates/polls, but in this case the app was named something like polls_app and the templates sub-directory something like polls_templates. Any other naming mismatch results in the same way.
I found this while trying to run tests in django - even though everything else worked (other than this specific, generic view), the tests failed with an error. Investigating that error led me to the tests runner code (see here or here) and loadTestsFromName in it.
So I guess that django relies on the object name (in the example above - it was searching for polls in polls_templates or something similar), but I couldn't find how this can be configured. Trying to debug it with pdb wans't great either, since I was getting way deep into django's source code.
I created a new app, with everything named the same, and now tests are running ok, and also the SolutionView, so having everything called by the same name solved the question for me.
I assume that there's a similar name-relying module in django's url which does the same thing.
I've read in a book that you should avoid doing this:
from .views import *
What I am currently doing is the following:
from . import views
My first question is, if this is the same thing just written differently?
My second question is if I should import it this way above or should I import every view separately?
from .views import (DetailView, EditView, DeleteView,
ListView, AnotherView, OneMoreView)
I mean it is bad practice because you import everything, even if you do not use it. If you have helper functions in your views, it imports those as well. If you go with the second option you must use the prefix views. before any view function/class. This can be a nuisance, therefore you should just import each view you would like to use, then you can just call the view.
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')
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