I wrote a little app in Django about a year ago, but I forgot it, so I will remember it while leaving it as a memorandum. I compared the latest tutorials in the official documentation because the Japanese translation of the tutorial is only 1.4, which is a fairly old release, but the article here I remember it was very helpful.
1.8 has already come out, but since I did it last time in 1.7, it will come in 1.7.
[sayamada@~]$cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=12.04
DISTRIB_CODENAME=precise
DISTRIB_DESCRIPTION="Ubuntu 12.04.5 LTS"
[sayamada@~]$uname -a
Linux XXXXXX 3.13.0-65-generic #106~precise1-Ubuntu SMP Fri Oct 2 22:07:14 UTC 2015 i686 i686 i386 GNU/Linux
[sayamada@~]$python -V
Python 2.7.9 :: Anaconda 2.0.1 (32-bit)
[sayamada@~]$pip list |grep -i django
Django (1.7.1)
django-bootstrap-form (3.1)
I can't get root on my company PC, so I use Anaconda to make the tea muddy. Ordinary people
$ pip install django==1.7.1
Isn't it okay?
Is one website equivalent to one project? First of all, I made a project.
[sayamada@git]$django-admin.py startproject demoPrj
[sayamada@git]$tree demoPrj/
demoPrj/
├── demoPrj
│ ├── __init__.py
│ ├── settings.py --The guy who writes the settings
│ ├── urls.py --The guy who writes the top routing
│ └── wsgi.py
└── manage.py --Execution script for management commands
It seems to work with sqlite by default, but I like the occupational pattern PostgreSQL, so I will change it. ** pyscopg2 ** I feel like I had to add it. If you don't like PostgreSQL, you can just use sqlite.
settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'DB name',
'USER': 'username',
'PASSWORD': '',
'HOST': 'localhost',
'PORT': '5432'
}
}
I'm Japanese so I'll change it.
settings.py
# LANGUAGE_CODE = 'en-us'
LANGUAGE_CODE = 'ja'
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Tokyo'
It seems that the site (project) consists of multiple ** applications **. Feeling like creating an app for each function and adding it? What is it?
[sayamada@git]$cd demoPrj/
[sayamada@demoPrj]$python manage.py startapp demoApp
[sayamada@demoPrj]$tree
.
├── demoApp --This directory has increased
│ ├── __init__.py
│ ├── admin.py
│ ├── migrations
│ │ └── __init__.py
│ ├── models.py --The one that defines the model
│ ├── tests.py
│ └── views.py --A guy who writes VIEW or logic entry points?
├── demoPrj
│ ├── __init__.py
│ ├── __init__.pyc
│ ├── settings.py
│ ├── settings.pyc
│ ├── urls.py
│ └── wsgi.py
└── manage.py
3 directories, 13 files
By the way, although it is not created by default, it seems that a large routing is defined in demoPrj / urls.py and the routing for each application is set individually, so ** demoApp / urls.py ** is created manually. ..
django defines a model (** User **) for account management in advance. Middleware for authentication is also available. This time, I created the authentication process in an application called ** Auth **, so I will add it to INSTALLED_APPS as well.
settings.py
INSTALLED_APPS = (
'django.contrib.admin',
'django.contrib.auth',★ With this
'django.contrib.contenttypes',★ I need this
:
'Authctl',★ This was made with startapp
:
LOGIN_URL = '/auth/login'
LOGIN_REDIRECT_URL = '/auth/home'
:
AUTHENTICATION_BACKENDS = (
'Authctl.AuthLogic.AuthBackEnd',
'django.contrib.auth.backends.ModelBackend',
)
It is included when the project is created. After that, specify the authentication logic in AUTHENTICATION_BACKENDS. Above is the authentication logic I made. I forgot why there are two. The view side of the page that requires authentication is as follows. This time, I created AuthLogic.py under Auth and decided to define a function called AuthBackend in it.
demoApp/views.py
# coding:utf-8
from django.http import HttpResponse
from django.contrib.auth.decorators import login_required
@login_required
def index(request):
return HttpResponse(u'index')
Methods with the login_required decorator are checked in advance to see if they are logged in. If you are not logged in, you will be redirected to ** LOGIN_URL ** in settings.py. This time it is / authctl / login, so you need to configure the routing to resolve it.
At the time of form.save, there is usually an error with NOT NULL constraint. You can pass an instance of Model with attributes excluded by ** instance = ** to the form constructor.
views.py
any_model_inst = AnyModel()
any_model_inst.excluded_any_ked = u"hoge"
form = AnyForm(request.POST, instance=any_model_inst)
form.save()
I don't know where the request object is.
#Decorator for checking if API access has permission
def check_access_permission(func):
import functools
@functools.wraps(func)
def wrapper(*args, **kwargs):
#The first argument is the request object
request = args[0]
print request.user
if check_any_func():
return HttpResponseForbidden()
# OK
return func(*args, **kwargs)
return wrapper
I wanted to see the header when I threw something like this
curl localhost:8000/api/ \
-H "Authorization: demo token" \
-X GET
It was like this
def check_access_permission(func):
import functools
@functools.wraps(func)
def wrapper(*args, **kwargs):
#The first argument is the request object
request = args[0]
#You can get it with this
print request.META.get('HTTP_AUTHORIZATION')
if False:
return HttpResponseForbidden()
return func(*args, **kwargs)
return wrapper
When I wanted to narrow down the value specified by REST, I made a Form just for Validation and Choice Fileld was just right.
forms.py
contact_choice = (
("TEL", "TEL"),
("Email", "Email"),
("FAX", "FAX"),
("Web", "Web"),
)
contact_way = forms.ChoiceField(label=u"Inquiry method", choices=contact_choice,required=False, initial=None)
With this, if you enter hoge in contact_way, you will die with is_valid ().
It was written properly in the Manual. It seems that there are various ways to display it after 1.7.
if form.is_valid():
something doing..
else:
# FromClass.There are errors in errors
# as_json()Can be retrieved in JSON format with
print form.errors.as_json()
sender_name = forms.CharField(label=u'Sender name', required=False, initial=u"default sender")
So, if you do this
sender_name = form.cleaned_data["sender_name"] # -> I expect "default sender" but None
It has become.
Looking at here, initial is the initial data on the display, and it seems that it cannot be obtained with cleaned_data. It looks like you need to override form.clean. There was a part that didn't move a little, so I finally replaced it with the following.
class DisplaySharerForm(forms.Form):
...
def clean(self):
cleaned_data = super(DisplaySharerForm, self).clean()
for key, value in cleaned_data.items():
if not value:
cleaned_data[key] = self.fields[key].initial
return cleaned_data
Recommended Posts