📜 ⬆️ ⬇️

Effective Django. Part 1


I present to you the translation of articles on Django from the site effectivedjango.com . I stumbled upon this site while studying this framework. The information posted on this resource seemed useful to me, but since I did not find a translation into Russian anywhere, I decided to do this good deed myself. This cycle of articles, I think, will be useful for web developers who are only taking the first steps in learning Django.



Table of contents


Introduction ⇧


Django is a popular, powerful Python framework. He has many " batteries ", and allows you to immediately start development . However, all this power means that you can write low-grade code that seems to work. So what is meant by Effective Django? By Effective Django, we mean the use of Django in such a way that the written code is coherent , tested and scalable . What does each of these words mean?

" Connected" code is a code that focuses on doing one thing, only one thing. This means that when you write a function or method, the code you write must do one thing and do it well.

This directly relates to writing test code : code that does a lot of things is often too difficult to test. When I catch myself thinking, “Well, this piece of code is too complicated to write tests for it — it's just not worth the effort” —that is a signal to go back and focus on simplification. The test code is a code that allows you to simply write tests for it; code that is easy to find problems.
')
Finally, we want to write scalable code . This means not only to scale it in terms of execution, but also to increase in terms of command and command understanding. Well-tested applications are easier for others to understand (and easier for them to change), which implies a greater opportunity to improve your application by adding new engineers.

My goal is to convince you of the importance of these principles, and provide examples of how, by following them, build a more robust Django application. I'm going to go through the process of building a contact management application one by one, talking about the solutions and testing strategy that I use.

These documents are a combination of notes and examples prepared for PyCon 2012, PyOhio 2012, and PyCon 2013, as well as for Eventbrite web development. I'm still working on combining them into one document, but I hope you find them useful.

Code samples for this tutorial are available on github . Feedback, suggestions and questions can be sent to nathan@yergler.net .
This document is available on the website , as well as in PDF and EPub formats .

Videos of this guide from PyCon can be viewed on YouTube .

Chapter 1. Getting Started ⇧


1.1. Your development environment


When talking about your development environment, there are three important things to keep in mind: isolation , predetermination, and similarity . Each of them is important and they all interact with each other in concert.

Isolation means that you cannot accidentally use tools or packages installed outside of your environment. This is especially important when this happens with something similar to Python packages with extensions written in C: if you use something installed at the system level and you don’t know about it, then when you deploy or distribute your code you may find that it does not work as intended. Tools like virtualenv can help create something similar to an isolated environment.

Your environment is predetermined if you are sure which version of your dependencies you rely on and whether you can surely reproduce the system environment.

And finally, the similarity with the production or development server environment means that the same operating system is installed everywhere (maybe even the same release ) and you use the same tools to configure your development environment and to configure your production environment. This is by no means a necessity, but if you build large and complex software, the similarity will be useful to make sure that all the problems that you can see on the “combat” server are replicable in the environment where you are developing. In addition, the similarity limits the scope of your code.

1.1.1. Insulation



1.1.2. Predestination



You can pinpoint the versions using either the PyPI version of the package, or a specific revision (SHA in git, revision number in Subversion, etc.). This ensures that you can get exactly the same version of the packages that you use when testing.

1.1.3. Similarity




1.2. Setting up your environment


1.2.1. Creating a clean workspace


Translator's Note:
First, create a directory ( tutorial ) in which we will work:

 ~$ mkdir tutorial ~$ cd tutorial ~/tutorial$ mkdir venv project 

Our virtual environment will be located in the venv directory, and the project directory will contain the Django project.

 ~/tutorial$ virtualenv --prompt="(venv:tutorial)" ./venv/ New python executable in ./venv/bin/python Installing setuptools............done. Installing pip...............done. ~/tutorial$ source ./venv/bin/activate (venv:tutorial)~/tutorial$ 


1.2.2. Creating a dependency file


Create a requirements.txt file in the tutorial directory with a single line (dependency) in it:

 Django==1.6.7 

Translator's Note:
In case you want to use the latest version of Django (1.7 - at the time of writing the translation) - instead of the line Django==1.6.7 just leave Django - pip installs the latest available version.

1.2.3. Installing dependencies


And now we can use pip to install dependencies:

 (venv:tutorial)~/tutorial$ pip install -U -r requirements.txt Downloadping/unpacking Django==1.6.7 (from -r requirements.txt (line 1)) Downloading Django-1.6.7.tar.gz (6.6MB): 6.6MB downloaded Running setup.py egg_info for package Django warning: no previously-included files matching '__pycache__' found under directory '*' warning: no previously-included files matching '*.py[co]' found under directory '*' Installing collected packages: Django Running setup.py install for Django changing mode of build/scripts-2.7/django-admin.py from 644 to 755 warning: no previously-included files matching '__pycache__' found under directory '*' warning: no previously-included files matching '*.py[co]' found under directory '*' changing mode of /home/nathan/p/edt/bin/django-admin.py to 755 Successfully installed Django Cleaning up... 


1.3. Beginning of the Django project


When a building is under construction, scaffolding is often used to maintain the structure until construction is completed. Scaffoldings can be temporary or they can serve as part of the foundation of a building, but despite this, they provide some support when you first start working.

Django, like many web frameworks, represents scaffolding for your development. This happens by making decisions and providing a starting point for your code, which allows you to focus on the problem you are trying to solve, and not on how to parse the HTTP request. Django provides scaffolding both for working with HTTP and for working with the file system.

HTTP scaffolding controls, for example, the conversion of an HTTP request to a Python object, and also provides tools for creating server responses more easily. File system scaffolding is different: it is a set of conventions for organizing your code. These agreements make it easier to add new engineers to the project, as engineers (hypothetically) already understand how the code is organized. In terms of Django, a project is the final product, and it combines one or more applications within it. In Django 1.4, the way projects and applications are placed on disk is changed, which makes it easier to disconnect and reuse applications in different projects.

1.3.1. Project creation


Django installs the django-admin.py script into the system to handle scaffolding tasks. To create project files, use the startproject task. We will define the name of the project and the name of the directory in which we want to place the project. Since we are already in an isolated environment, we can simply write:

Translator's Note:
Let's go to the ~/tutorial/project/ directory and in the future we will work only from this directory (by $ we will mean ~/tutorial/project/$ ):

 (venv:tutorial)~/tutorial/$ cd project 

 (venv:tutorial)$ django-admin.py startproject addressbook . 

The created project has the following structure

 manage.py ./addressbook __init__.py settings.py urls.py wsgi.py 


1.3.2. Project scaffolding




1.3.3. Create application


 (venv:tutorial)$ python ./manage.py startapp contacts 

The created application has the following structure:

 ./contacts __init__.py models.py tests.py views.py 


Translator's Note:
Currently, our ~/tutorial/ directory contains the dependencies file ( requirements.txt ), the virtual environment directory ( venv/ ), one project ( project/addressbook ), and one application ( project/contacts ) and has the following contents:

 ~/tutorial/ requirements.txt venv/ ... project/ manage.py addressbook/ __init__.py settings.py urls.py wsgi.py contacts/ __init__.py models.py tests.py views.py 


Chapter 2. Using the model ⇧


2.1. Configuring the database


Django supports out of the box MySQL, PostgreSQL, SQLite3, and Oracle. SQLite3 has been included in Python since version 2.5, so we will use it in our project (for simplicity). If you want to use MySQL, for example, then you need to add mysql-python to your requirements.txt .

To use SQLite as a database, edit the DATABASES definition in the addressbook/settings.py file. The settings.py file contains the Django settings for our project. It has several settings that you must specify - for example, DATABASES - as well as other, optional, settings. Django sets up some of the settings itself when it generates a project. The documentation contains a complete list of settings . In addition, you can add your own settings if necessary.

To use SQLite, we need to specify the engine ( ENGINE ) and the name of the database ( NAME ). SQLite interprets the database name as the file name for the database:

 DATABASES = { 'defaults': { 'ENGINE': 'django.db.backends.sqlite3,' # 'postgresql_psycopg2', 'mysql', 'sqlite3' or 'oracle'. 'NAME': os.path.join(BASE_DIR, 'address.db'), 'USER': '', # Not used with sqlite3. 'PASSWORD': '', # Not used with sqlite3. 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. 'PORT': '', # Set to empty string for default. Not used with sqlite3. } } 

Notice that the database engine is specified as a string, and not as a direct reference to a Python object. This is done for the reason that the settings file should be easily imported without causing third-party effects . You should avoid adding import calls to this file.

You rarely have to import the settings file directly: Django imports it for you, and makes the settings available as django.conf.settings . You typically import settings from django.conf :

 from django.conf import settings 


2.2. Creating a model


Django models display (roughly) database tables, and provide a place to encapsulate business logic. All models are descendants of the Model base class and contain definition fields. Let's create a simple Contacts model for our application in the contacts/models.py :

 from django.db import models class Contact(models.Model): first_name = models.CharField( max_length=255, ) last_name = models.CharField( max_length=255, ) email = models.EmailField() def __str__(self): return ' '.join([ self.first_name, self.last_name, ]) 

Django provides a set of fields for displaying data types and various validation rules. For example, EmailField , which we used, is a mapping to a column with type CharField , but adds validation data.

After you have created a model, you need to add new tables to your database. The Django syncdb looks at the installed models and creates (if necessary) tables for them:

Translator's Note:
Django will offer to create a superuser for Android, which is enabled by default in this version. Take advantage of his offer.
Translator's Note:
Since Django 1.7, native support for migrations has been added to the framework and the syncdb been deprecated. So be so kind as to use the migrate syncdb instead of syncdb .

 (venv:tutorial)$ python ./manage.py syncdb Creating tables ... Creating table django_admin_log Creating table auth_permission Creating table auth_group_permissions Creating table auth_group Creating table auth_user_groups Creating table auth_user_user_permissions Creating table auth_user Creating table django_content_type Creating table django_session You just installed Django's auth system, which means you don't have any superusers defined. Would you like to create one now? (yes/no): yes Username (leave blank to use 'bjakushka'): Email address: Password: Password (again): Superuser created successfully. Installing custom SQL ... installing indexes ... Installed 0 object(s) from 0 fixture(s) (venv:tutorial)$ 
Translator's Note:
If you are using Django version 1.7 and higher, the output will be as follows:

 (venv:tutorial)$ python ./manage.py migrate Opperation to perform: Apply all migrations: admin, contenttypes, auth, sessions Running migrations: Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying sessions.0001_initial... OK (venv:tutorial)$ 

However, our table with contacts is nowhere to be seen. The reason for this is that we still need to tell the project to use the application.

Setup INSTALLED_APPS contains a list of applications used in the project. This list contains strings that display Python packages. Django will import each of the specified packages, and then watch the models module. Let's add our contacts application to the project settings ( addressbook/settings.py ):

 INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'contacts', ) 

After that, run syncdb again:
Translator's Note:
For Django version 1.7 and higher, you will need to first run the makemigrations command - to create migrations based on changes in models, and then execute the migrate command - in order to apply the created migrations.

 (venv:tutorial)$ python ./manage.py syncdb Creating tables ... Creating table contacts_contact Installing custom SQL ... Installing indexes ... Installed 0 object(s) from 0 fixture(s) (venv:tutorial)$ 
Translator's Note:
Conclusion for Django 1.7 and higher:

 (venv:tutorial)$ python ./manage.py makemigrations Migrations for 'contacts': 0001_initial.py: - Create model Contact (venv:tutorial)$ python ./manage.py migrate Opperation to perform: Apply all migrations: admin, contenttypes, sessions, auth, contacts Running migrations: Applying contacts.0001_initial... OK (venv:tutorial)$ 

Notice that Django creates a table called contacts_contact : by default, Dj ango gives tables a name using a combination of application name and model name. You can change this with options in the Meta model.

2.3. Interaction with the model


Now that the model is synchronized with the database, we can interact with it using an interactive shell:

 (venv:tutorial)$ python ./manage.py shell Python 2.7.3 (default, Mar 14 2014, 11:57:14) [GCC 4.7.2] on linux2 Type "help", "copyright", "credits" or "license" for more information. (InteractiveConsole) >>> from contacts.models import Contact >>> Contact.objects.all() [] >>> Contact.objects.create(first_name='Nathan', last_name='Yergler') <Contact: Nathan Yergler> >>> Contact.objects.all() [<Contact: Nathan Yergler>] >>> nathan = Contact.objects.get(first_name='Nathan') >>> nathan <Contact: Nathan Yergler> >>> print nathan Nathan Yergler >>> nathan.id 1 

Here used a few new pieces. First, the manage.py shell command runs the Python interactive shell for us with the correct paths for Django. If you try to start the Python interpreter and just import your applications, an exception will be thrown, because Django does not know which settings to use, and cannot display the model instances on the database.

Secondly, the property objects our model were used here. This is the model manager . So, if a single instance of a model is an analogy for a row in a database, then the model manager is an analogy for a table. By default, the model manager provides query functionality and can be configured. When we call all() , filter() or the manager itself, the QuerySet object is returned. QuerySet is an iterable object and loads data from the database as needed.

And the last - the field with the name id , which we did not define in our model, was used above. Django adds this field as the primary key for the model, but only if you yourself have not determined which field will be the primary key .

2.4. Writing tests


In our model, one method is defined, __str__ , so it is time to write the tests. The __str__ method will be used in just a few places, and it is quite possible that it will be fully shown to the end user . For this method it is worth writing a test, while we understand how it works . Django created the tests.py file when it created the application, so we will add the first test to this file, the contacts application.

 from django.test import TestCase from contacts.models import Contact class ContactTests(TestCase): """Contact model tests.""" def test_str(self): contact = Contact(first_name='John', last_name='Smith') self.assertEquals( str(contact), 'John Smith', ) 

You can run tests for your application using the manage.py test command:

 (venv:tutorial)$ python ./manage.py test 

If you run this, you will see that it has completed about 420 tests. This is surprising, since we wrote only one. This happened because, by default, Django runs tests for all installed applications. When you added the contacts application to our project, you could see that several built-in Django applications were added by default. An additional 419 tests were taken from there.
Translator's Note:
In our case (when using Django version 1.6.7), the previous paragraph is somewhat outdated: only one test will start - the one we created. The output of the command will be as indicated below.

If you want to run tests for a specific application, enter the name of the application in the command:

 (venv:tutorial)$ python manage.py test contacts Creating test database for alias 'default'... . ---------------------------------------------------------------------- Ran 1 tests in 0.001s OK Destroying test database for alias 'default'... (venv:tutorial)$ 

Another interesting thing to note before moving on is the first and last line of output: Creating test database and Destroying test database . Some tests require access to the database, and since we don’t want to interfere with test data with “real” (for various reasons, not the least of which is predetermination), Django helpfully creates a test base for us before running the tests. Essentially, a new database is created, and then syncdb started for it. If the test class is a descendant of the TestCase class (like ours), Django will also reset the data to default values ​​after running each test, so the changes in one of the tests will not affect the others.

2.5. Summary



Translator's Note:
In order to test our, still empty, application, you need to run the following command:

 (venv:tutorial)$ python ./manage.py runserver 0.0.0.0:8080 

This will launch the built-in server, the functionality of which Django graciously provides us. The parameters after the runserver indicate the ip-address and port that the server will listen to. In our case, the server will receive requests from all ip-addresses when accessing port 8080.

I use to develop a home server with an internal IP of 192.168.1.51. So in order to see the result of the development server in the browser, I go to http://192.168.1.51:8080/ . You must substitute the address of your server.




Do you think it is necessary to continue translating the remaining chapters? Was the translation helpful to you?
I would be glad to constructive criticism in the comments.
I ask you to report errors and inaccuracies of the translation in a personal message.

The image used at the beginning of the post was created as a variation of the image of the user MaGIc2laNTern

Source: https://habr.com/ru/post/240463/


All Articles