Split views.py in several files
Question:
My views.py
has become too big and it’s hard to find the right view.
How do I split it in several files and then import it? Does it involve any speed loss?
Can I do the same with models.py
?
Answers:
Basically, you can put your code, whereever you wish. Just make sure, you change the import statements accordingly, e.g. for the views in the urls.py
.
Not knowing your actual code its hard to suggest something meaningful. Maybe you can use some kind of filename prefix, e.g. views_helper.py
, views_fancy.py
, views_that_are_not_so_often_used.py
or so …
Another option would be to create a views
directory with an __init__.py
, where you import all subviews. If you have a need for a large number of files, you can create more nested subviews as your views grow …
In Django everything is a Python module (*.py). You can create a view folder with an __init__.py
inside and you still will be able to import your views, because this also implements a Python module. But an example would be better.
Your original views.py
might look like this :
def view1(arg):
pass
def view2(arg):
pass
With the following folder/file structure it will work the same :
views/
__init__.py
viewsa.py
viewsb.py
viewsa.py
:
def view1(arg):
pass
viewsb.py
:
def view2(arg):
pass
__init__.py
:
from viewsa import view1
from viewsb import view2
The quick explanation would be: when you write from views import view1
Python will look for view1 in
-
views.py
, which is what happens in the first (original) case
-
views/__init__.py
, which is what happens in the second case. Here, __init__.py
is able to provide the view1 method because it imports it.
With this kind of solution, you might have no need to change import
or urlpattern
s arguments in urls.py
If you have many methods in each new view file, you might find it useful to make the imports in views/__init__.py
use *
, like this:
from viewsa import *
from viewsb import *
I actually don’t know about speed issues (but I doubt there are any).
For Models it might be a bit difficult.
Since Django just expects a view to be a callable object, you can put then wherever you like in your PYTHONPATH. So you could for instance just make a new package myapp.views and put views into multiple modules there. You will naturally have to update your urls.py and other modules that reference these view callables.
Simple answer: Yes.
Best is to make a directory called views and then in your urls.py do:
import views
...
url(r'^classroom$', views.school.klass, name="classroom"),
I’ve had to do this before (for clarities sake)
The way I did this was to create a views
directory, then, in that, create a file called __init__.py
Now, when you’re calling in your urls.py
, you simply need to add another part
For example, previously, you may have called:-
url(r'^calendar/(?P<year>dddd)/$', 'myproject.calendar.views.year')
url(r'^calendar/(?P<year>dddd)/(?P<user>[a-z]+)/$', 'myproject.calendar.views.year_by_user')
You can now call something along the lines of
url(r'^calendar/(?P<year>dddd)/$', 'myproject.calendar.views.year.index')
url(r'^calendar/(?P<year>dddd)/(?P<user>[a-z]+)/$', 'myproject.calendar.views.year.user')
This is, of course, assuming that you had views/year.py
containing the functions index
and user
😉
I split almost all views in my apps into a views folder (with an init.py of course). I do not, however, import all of the subviews in the init.py like some of the answers have suggested. It seems to work just fine.
I’ve been playing with putting this in my init.py:
import os
currPath = os.path.realpath(os.path.dirname(__file__))
dirFiles = []
for root, dirs, files in os.walk(currPath):
for name in files:
if name.endswith('.py') and not name.startswith('_'):
dirFiles.append(name.strip('.py'))
for f in dirFiles:
exec("from %s import %s" % (f,f))
I’m still new to python, so I’m still looking at what effect it has on speed/security/ease of use.
Suppose if you have a file named: password_generator.py
then inside views.py
add: from password_generator import *
Then you can call that module’s function from views.py
.
Just for sharing, I had a bit of issues with Vincent Demeester’s answer. Everything is fine except in init.py file, I have to write in this way:
__init__.py:
from .viewsa import *
from .viewsb import *
This way I still don’t need to change my import
method in urls.py. I am on Python 3.6.1 and Django 1.11.4.
Vincent Demeester‘s answer is superb! but for me addicted‘s answer worked like a charm.
I faced difficulties in migrating database. The error indicates the line where the first model is imported and says could not recognize my app module. Searched a lot but could not find a solution but later on I imported the model like this:
from ..models import ModelName
It worked!!
My views.py
has become too big and it’s hard to find the right view.
How do I split it in several files and then import it? Does it involve any speed loss?
Can I do the same with models.py
?
Basically, you can put your code, whereever you wish. Just make sure, you change the import statements accordingly, e.g. for the views in the urls.py
.
Not knowing your actual code its hard to suggest something meaningful. Maybe you can use some kind of filename prefix, e.g. views_helper.py
, views_fancy.py
, views_that_are_not_so_often_used.py
or so …
Another option would be to create a views
directory with an __init__.py
, where you import all subviews. If you have a need for a large number of files, you can create more nested subviews as your views grow …
In Django everything is a Python module (*.py). You can create a view folder with an __init__.py
inside and you still will be able to import your views, because this also implements a Python module. But an example would be better.
Your original views.py
might look like this :
def view1(arg):
pass
def view2(arg):
pass
With the following folder/file structure it will work the same :
views/
__init__.py
viewsa.py
viewsb.py
viewsa.py
:
def view1(arg):
pass
viewsb.py
:
def view2(arg):
pass
__init__.py
:
from viewsa import view1
from viewsb import view2
The quick explanation would be: when you write from views import view1
Python will look for view1 in
-
views.py
, which is what happens in the first (original) case -
views/__init__.py
, which is what happens in the second case. Here,__init__.py
is able to provide the view1 method because it imports it.
With this kind of solution, you might have no need to change import
or urlpattern
s arguments in urls.py
If you have many methods in each new view file, you might find it useful to make the imports in views/__init__.py
use *
, like this:
from viewsa import *
from viewsb import *
I actually don’t know about speed issues (but I doubt there are any).
For Models it might be a bit difficult.
Since Django just expects a view to be a callable object, you can put then wherever you like in your PYTHONPATH. So you could for instance just make a new package myapp.views and put views into multiple modules there. You will naturally have to update your urls.py and other modules that reference these view callables.
Simple answer: Yes.
Best is to make a directory called views and then in your urls.py do:
import views
...
url(r'^classroom$', views.school.klass, name="classroom"),
I’ve had to do this before (for clarities sake)
The way I did this was to create a views
directory, then, in that, create a file called __init__.py
Now, when you’re calling in your urls.py
, you simply need to add another part
For example, previously, you may have called:-
url(r'^calendar/(?P<year>dddd)/$', 'myproject.calendar.views.year')
url(r'^calendar/(?P<year>dddd)/(?P<user>[a-z]+)/$', 'myproject.calendar.views.year_by_user')
You can now call something along the lines of
url(r'^calendar/(?P<year>dddd)/$', 'myproject.calendar.views.year.index')
url(r'^calendar/(?P<year>dddd)/(?P<user>[a-z]+)/$', 'myproject.calendar.views.year.user')
This is, of course, assuming that you had views/year.py
containing the functions index
and user
😉
I split almost all views in my apps into a views folder (with an init.py of course). I do not, however, import all of the subviews in the init.py like some of the answers have suggested. It seems to work just fine.
I’ve been playing with putting this in my init.py:
import os
currPath = os.path.realpath(os.path.dirname(__file__))
dirFiles = []
for root, dirs, files in os.walk(currPath):
for name in files:
if name.endswith('.py') and not name.startswith('_'):
dirFiles.append(name.strip('.py'))
for f in dirFiles:
exec("from %s import %s" % (f,f))
I’m still new to python, so I’m still looking at what effect it has on speed/security/ease of use.
Suppose if you have a file named: password_generator.py
then inside views.py
add: from password_generator import *
Then you can call that module’s function from views.py
.
Just for sharing, I had a bit of issues with Vincent Demeester’s answer. Everything is fine except in init.py file, I have to write in this way:
__init__.py:
from .viewsa import *
from .viewsb import *
This way I still don’t need to change my import
method in urls.py. I am on Python 3.6.1 and Django 1.11.4.
Vincent Demeester‘s answer is superb! but for me addicted‘s answer worked like a charm.
I faced difficulties in migrating database. The error indicates the line where the first model is imported and says could not recognize my app module. Searched a lot but could not find a solution but later on I imported the model like this:
from ..models import ModelName
It worked!!