Splitting a large views.py into smaller pieces - python

I have a huge views.py file and so I split it up into views.py and admin_views.py. Since the functions tend to depend on functions in the other file, I did the following:
# views.py
from admin_views.py import a,b,c
# admin_views.py
from views.py import d,e,f
I found out this results in a circular dependency, and the imports don't work.
What is the best approach to solve this problem? I know I could put import statements at the bottom of the file, but that doesn't seem too elegant.

First, make a view package.
--view
|_ __init__.py
|_ main.py # previewsly view.py
|_ admin.py # previewsly view_admin.py
Then, add a new file that will include the part you need to import in main or admin:
--view
|_ __init__.py
|_ main.py
|_ admin.py
|_ base.py
Then move the imports so they import both from base.
Eventually, in __init__, import stuff you need from main et admin. Every thing you import in __init__ will be available to import using from views import stuff.

You shouldn't have core logic in views.py files. You should put this core logic into other python files (some logic is appropriate for the models.py or forms.py) and import it into the views.py. Basically the only place you should be importing a views.py file in most cases is into the urls.py file. The urls.py file also can be in a format such as:
urlpatterns = patterns('management.views',
#url(r'^index/$', direct_to_template, {'template': 'stats/index.html'},
# name="index"),
url(r'^pay_invoices/$', 'pay_invoices', name='pay_invoices'),
)
This makes it so that a views.py function basically never need be imported anywhere.

Is the circular import to do with a foreign key relationship?
If so you can define the class as a string.
item = models.ForeignKey("SomeOtherClass")
Failing that I normally extract out the logic to a third file like e-satis suggested.

Related

How to split django 'models.py', 'views.py', 'serializers.py' files into multiple files

Hello,
For educational purposes, I am building a django app with multiple models and relationships.
According to the official tutorial and many implementations I found online, the database models and serializers as well as views are all defined in single files: "models.py", "serializers.py", and "views.py".
So, the project directory looks as follows:
> my_app
> migrations
> __init__.py
> admin.py
> models.py
> apps.py
> serializers.py
> tests.py
> urls.py
> views.py
Depending on how many models are included in the app, those files may grow to hundreds or even thousands lines of code.
As a result, developing and maintaining the application becomes extremely challenging.
I would like to split these files so that every model (and coresponding serializer and view) will be defined in a separate per-model file. As follows:
> my_app
> migrations
> models
> __init__.py
> model1.py
> model2.py
> model3.py
> model4.py
> serializers
> __init__.py
> model1_serializers.py
> model2_serializers.py
> model3_serializers.py
> model4_serializers.py
> views
> __init__.py
> model1_views.py
> model2_views.py
> model3_views.py
> model4_views.py
> __init__.py
> admin.py
> apps.py
> tests.py
> urls.py
I encountered some difficulties in splitting these files and have not yet found an optimal solution.
The Problem
In order to define a serializer -> corresponding model should be imported.
And in order to define a view -> corresponding model and serializers should be imported.
There are some difficulties importing objects from models/files located in the same level as the parent directoriey.
For example: Importing model to serializers/model1_serializers.py results an error
from models.model1 import Model1 # error: Unresolved reference 'models'
from my_app.models.model1 import Model1 # error: Unresolved reference 'my_app'
What I have tried
Mark project directory as source in pycharm - After marking "my_app" folder as source the following import works. But running the code outside of pycharm (cmd for example) results import errors.
from models.model1 import Model1
Adding the project direcrtory to sys.path - sys.path contains a list of directories that the interpreter will search in for the required module. So adding the following lines should make import possible (in file my_app/views/model1_views.py), but it doesnt work, pycharm still marks the import lines as errors. Do you know where is my mistake?
import os
from sys import path
path.append(os.path.dirname(os.path.dirname(__file__)))
from my_app.models.model1 import Model1
I would very appreciate if you could explain my mistake and propose a solution for spliting those files, thank you!
Don't know if it's advisable to create a new view and serializer file for each model, it kinda makes things messy as the app grows.
If it's just the model you can do what's mentioned in the Django docs
Where you can remove the models.py file and instead create a models directory and have your different model files there as such: myapp/models/, myapp/models/first_model.py etc.
But if you basically want to separate concerns you can create a new Django app for each function/concern, like this:
> my_app_one
> migrations
> __init__.py
> admin.py
> models.py
> apps.py
> serializers.py
> tests.py
> urls.py
> views.py
> my_app_two
> migrations
> __init__.py
> admin.py
> models.py
> apps.py
> serializers.py
> tests.py
> urls.py
> views.py
This should fix your import issue.
Instead of making multiple models, serializers and views files make multiple apps!
That way is more organized and also you can link apps together and import whatever you like from other applications!
You need to add a package __init__.py file in my_app, and import from there:
from my_app import models
models.Model1.objects.all()
This tells Python that my_app is a package, and makes the submodules importable from it.
You thus structure your project as:
my_app/
__init__.py
models/
__init__.py
model1.py
model2.py
...
serializers/
__init__.py
...
views/
__init__.py
...
...
And then import from the my_app package.
Note that often models, serializers and views are grouped per application, not per model. So you make app1, app2, etc. subpackages, and place the models, serializers and views for that app in there.

Grouping imports together from subdirectories

Say I have a django app where I'm using folders and subfolders to organize my models:
app
models
__init__.py
accounts
user.py
profile.py
social
facebook.py
twitter.py
linkedin.py
admin.py
apps.py
urls.py
views.py
My __init__.py file is as follows:
from .accounts.user import User
from .accounts.profile import Profile
from .social.facebook import Facebook
from .social.twitter import Twitter
from .social.linkedin import LinkedIn
Is there any way to group these imports together or make the code shorter?
E.g. (obviously doesn't work)
from . import *
# or
from .accounts import *
from .social import *
Nope. The package that contains a module will not necessarily know anything about the contents of the module, so in your case accounts doesn't know anything about the User class in accounts.user. There isn't any general way to group things more than what you're already doing.

apps/goods/models.py 'apps.goods.models' apps is not package?

my project directories is:
apps/
goods/
models.py
views.py
base.py
trades/
users/
__init__.py
apps/goods/base.py
from django.views.generic.base import View
from apps.goods.models import Goods
class GoodsListView(View):
def get(self, request):
json_list = []
goods = Goods.objects.all()[:10]
for good in goods:
# json_dict = {}
# json_dict['name'] = good.name
# json_dict['category'] = good.category.name
# json_dict['market_price'] = good.market_price
# json_dict['add_time'] = good.add_time
# json_list.append(json_dict)
from django.forms.models import model_to_dict
for good in goods:
json_dict = model_to_dict(good)
json_list.append(json_dict)
from django.http import HttpResponse
import json
return HttpResponse(json.dumps(json_list), content_type='application/json')
i'm debug base.py not get data, but get the error:
from apps.goods.models import Goods
ModuleNotFoundError: No module named 'apps.goods'; 'apps' is not a package
and, i remove 'apps' in 'apps.goods.models', get the error:
from goods.models import Goods
ModuleNotFoundError: No module named 'goods'
env:
pycharm-2017.2
django-1.11.6
why get the error?
Use just from .models import Goods (look at "." before models - it means the module is from current folder ).Because base.py and models.py are in same folder (same app) so you dont need to specify from which app you want to import models. Just simply include it like this.
But if you want to import models from other apps, you should to make apps to be package.In Goods app folder add __init__.py.
Structure should look like:
apps/
goods/
__init__.py
models.py
views.py
base.py
trades/
users/
__init__.py
Than use from goods.models import Goods or from apps.goods.models import Goods
As in the others' comments, you need to create the init file in the folder that should be considered a package. It's called __init__.py however. You have one of these files in apps, make sure you have it in apps/goods as well.
If you still have the same problem, make sure your configuration in Django is correct, i.e. the folder above apps is loaded

Where to implement python classes in Django?

I'm learning Django on my own and I can't seem to get a clue of where I implement a regular Python class. What I mean is, I don't know where do the Python classes I write go. Like they go in a separate file and then are imported to the views.py or are the classes implemented inside the views.py file?
Example I want to implement a Class Alphabet, should I do this in a separate file inside the module or just implement the functions inside the views.py file?
I don't know where do the Python classes I write go. Like they go in
a separate file and then are imported to the views.py.
Example I want to implement a Class Alphabet.
It's just a matter of getting your import statement correct:
django_proj1/
django_proj1/
myapp/
myclasses.py
views.py
Then in your view:
#myapp/views.py:
from myapp.myclasses import Alphabet
Or, you could do it like this:
django_proj1/
django_proj1/
myclasses.py
myapp/
views.py
And in your view:
#myapp/views.py:
from django_proj1.myclasses import Alphabet
Response to comment:
And after I successfully imported my class, how do I pass the
attributes to an HTML template?
The following is straight from the official django tutorial.
myapp/views.py:
from django.shortcuts import render
from django.http import HttpResponse
from myapp.myclasses import Alphabet #<*** Import your class.
from django.template import RequestContext, loader #<*** Import stuff for the template.
# Create your views here.
def index(request):
alph = Alphabet()
result = alph.change('hello') #Your class produces some result here.
template = loader.get_template("myapp/index.html")
context = RequestContext(request, {
'x' : result #<*** Make a variable named x available in your template.
})
return HttpResponse(template.render(context))
The directory structure looks like this:
django_proj1/
django_proj1/
myapp/
myclasses.py
views.py
templates/ <***Create this directory
myapp/ <***Create this directory
index.html <***Create this file
myapp/templates/myapp/index.html:
{% if x %}
<div>The result you requested was: {{x}}</div>
{% else %}
<div>Sorry, couldn't get the result.</div>
{% endif %}
myapp/myclasses.py:
class Alphabet:
def change(self, word):
return word + 'Z'
Start the server:
.../my_django_projects/django_proj1$ python manage.py runserver
url in your browser:
http://localhost:8000/myapp/
You should see:
The result you requested was: helloZ
If you comment out the following line in myapp/views.py:
context = RequestContext(request, {
#'x' : result #<*** Make a variable named x available in your template.
})
Then the template will send the else portion of index.html to the browser:
Sorry, couldn't get the result.
django_proj1/django_proj1/urls.py:
from django.conf.urls import patterns, include, url
from django.contrib import admin
from . import views
urlpatterns = patterns('',
# Examples:
# url(r'^$', 'dj1.views.home', name='home'),
# url(r'^blog/', include('blog.urls')),
url(r'^admin/', include(admin.site.urls)),
url(r'^myapp/', include('myapp.urls')),
)
django_proj1/myapp/urls.py:
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'^$', views.index, name='index'),
]
Django is just Python at the end of the day.
You can create new modules anywhere in your project, and import them into your views, models, urls, etc. This is often how you'd organize general utils (utils.py).
You can deliver data to your views in a few ways, for instance:
from your_module import some_object
class MyView(TemplateView):
template_name = 'some_template.html'
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['my_data'] = some_object.some_method()
return context
And in some_template.html: {{ my_data }}
It depends on the scope of the Alphabet class. If it is a utility class then I would suggest to put in a utils.py file, for example. But it is perfectly fine to have classes in the views.py file, mainly those dealing with UI processing. Up to you.
Distinct to similar frameworks, you can put your Python code anywhere in your project, provided you can reference them later by their import path (model classes are partially an exception, though):
Applications are referenced by their import path (or an AppConfig import path). Although there's some magic involving test.py and models.py, most of the times the import / reference is quite explicit.
Views are referenced by urls.py files, but imported as regular python import path.
Middlewares are referenced by strings which denote an import path ending with their class name.
Other settings you normally don't configure are also full import paths.
The exception to this explicitness is:
models.py, test.py, admin.py : They have special purposes and may not exist, providing:
You will not need any model in your app, and will provide an AppConfig (instead of just the app name) in your INSTALLED_APPS.
You will not rely on autodiscovery for admin classes in your app.
You don't want to make tests on your app, or will specify a non-default path for your app-specific test command run.
templates and static files: your project will rely on per-app loaders for your static files and for your templates files, and ultimately there's a brute-force search in each of your apps: their inner static/ and templates/ directories, if exist, are searched for those files.
Everything else is just normal python code and, if you need to import them from any view, you just do a normal import sentence for them (since view code is imported with the normal Python import mechanism).

Can you name a Django app: urls

I'm trying to use this line in urls.py:
from mysite.urls.views import Index
However, Django is saying
ImportError at /
No module named views
I think that is because it's going into /mysite/mysite/urls.py and not /mysite/urls/views.py
The structure is like this (omitted the uninvolved files):
mysite/
templates/
mysite/
settings.py
urls.py
urls/
views.py
manage.py
I found out that was my problem. I followed How to change the name of a Django app? and my problems went away. Long story short, don't name your app: urls.
Add an empty __init__.py inside your urls folder. Why that should work? Take a look here and here
Absolute imports may be what you need:
from __future__ import absolute_import
Credits to this answer: https://stackoverflow.com/a/4931577/1028012

Categories

Resources