运行环境:python2.7(后期修改python3.5.2) django1.7 (后期修改django1.10)ubuntu14.10
本文糅合了tango_with_django(python2.7, django1.7)
以及djangoByExample(python3.5.2,django1.8)两本书,力求吸收两者优点的部分;
写在前面:先感受下django的目录结构,有个大概框架的概念:django首先是一个项目文件夹比如project(这是整个根目录),项目文件夹project下还有一个同名的project文件夹,里面有setting.py等文件,用来对整个项目的改变或设置参数,而通常还要在根目录下创建APP文件夹比如app1,app2,app3等等,每个app文件夹下都有对应的init.py,models.py,tests.py, views.py,admin.py,等文件。这里要注意项目包含app的概念,在project下修改的文件,作用于整个项目,当然会影响到所有app,比如对数据库的修改和建立。
(PS:所有关于setting.py的设置均可参照官方文档:https://docs.djangoproject.com/en/1.8/ref/settings/)
PPS:大写序号按实际开发顺序调整步骤,数组序号是tango_with_django中的步骤
bug solved:9.4.4 写url映射的时候,‘^register/$’ 注意这里/不能丢,要不无法映射
一 1,创建django项目:
django-admin.py startproject tango_with_django_project
这样会创建一个名叫tango_with_django_project的目录标注1,目录下有两个文件,manager.py以及同名目录tango_with_django_project标注2,同名目录下有init.py,settings.py,urls.py,wsgi.p四个文件。
项目创建好了之后,可是尝试运行下:
python manage.py runserver
再用浏览器访问127.0.0.18080,怎么样是不是很简单~!
二 2,创建django的应用:
步骤一:进入工程目录,即标注1的目录,
python manager.py startapp rango
这样就创建了名为rango的目录,目录下有5个文件:
- init.py,和我们前面说过的功能一样.
- models.py,一个存储你的应用中数据模型的地方 - 在这里描述数据的实体和关系.
- tests.py,存储你应用的测试代码.
- views.py,在这里处理用户请求和响应.
- admin.py,在这里你可以向Django注册你的模型,它会为你创建Django的管理界面.
步骤二:在工程同名目录下即标注2,找到settings.py文件,其中的INSTALLED_APPS元组.在元祖的最后面增加rango,目的是让工程识别应用。
三 7,模型与数据库的使用(6+3步):
zero:先通过数据库管理软件手动创建一个数据库,再考虑下面的步骤连接
first:settings.py(注意这里的setting.py是在工程目录下的工程文件夹里,因为要数据库的配置是要适应整个项目的,所以在这里配置)里添加一个叫做DATABASES的字典(sqlite是django自动添加的)(PS:所有关于setting.py的设置均可参照官方文档:https://docs.djangoproject.com/en/1.8/ref/settings/):
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
这里贴上博主连本地mysql的配置:
DATABASES = {
'default': {
# 'ENGINE': 'django.db.backends.sqlite3',
# 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
#配置使用mysql数据库
'ENGINE': 'django.db.backends.mysql',
'NAME': 'blogByDjango',
'USER': 'root',
'PASSWORD': '就不告诉你~',
'HOST': '127.0.0.1',
'PORT': '3306',
}
}
second:rango/models.py里定义模型,注意类继承models.MODEL;以及ForeignKey;还有unicode()方法(python3用的是str()方法,表示模型实例的名字),还有class Meta(写入到数据库中的表名称):
third:如果第一次用连数据库都没有创建,则创建迁移文件是可能报错:
$ python manage.py makemigrations rango
$ python manage.py migrate #将这些改变更新到数据库中
如果报错是找不到mysqldb,处理见博主博客(亲测有效),链接:http://blog.youkuaiyun.com/happy_bigqiang/article/details/54345356
forth:如果之前已经有数据库表,只是对其改动,则:
$ python manage.py makemigrations rango #为这些修改创建迁移文件(这里的迁移文件其实是一个记录文件,类似git的记录,并没有真正写入数据库,真正更新到数据库的是migrate命令)
$ python manage.py migrate #将这些改变更新到数据库中
fifth:设置输入数据脚本,比如create_data.py:
import django
django.setup()
from rango.models import Category, Page
def populate():
def add_cat(name):
c = Category.objects.get_or_create(name=name)[0]
return c #get_or_create返回的是(object,created)并且代替了 c=Category(name=name)
#c.save()
def add_page(cat, title, url, views=0):
p = Page.objects.get_or_create(category=cat, title=title, url=url, views=views)[0]
return p
python_cat = add_cat('Python')
add_page(cat=python_cat,
title="Official Python Tutorial",
url="http://docs.python.org/2/tutorial/")
if __name__ == '__main__':
populate()
以上基本的数据库创建已完成,6,7步介绍创建django自带的数据库后台管理系统:
sixth:创建管理员来管理数据库:
$ python manage.py createsuperuser
根据提示输入用户名密码邮箱之后,启动django项目;然后在浏览器中输入
http://127.0.0.1:8000/admin
输入刚刚设置的用户名和密码,会发现白得一管理后台,尽管现在功能还很少,这就是django的特色吧;
seventh:设置管理界面(把数据库的表加载进来),rango/admin.py文件中:
from django.contrib import admin
from rango.models import Category, Page
admin.site.register(Category)
admin.site.register(Page)
eighth:定制你的数据库后台管理:
你需要修改rango/admin.py文件,创建PageAdmin类,这个类继承自admin.ModelAdmin;在PageAdmin类里,加入list_display = (‘title’, ‘category’, ‘url’);最后注册PageAdmin类到Django管理界面.需要修改admin.site.register(Page).在Rango的admin.py文件里修改成admin.site.register(Page, PageAdmin)
例子来自djangoByExample:
from django.contrib import admin
from .models import Post
# Register your models here.
class PostAdmin(admin.ModelAdmin):
list_display = ('title','slug','author','publish','status')
#后台右侧实现过滤功能
list_filter = ('status','author','publish','created')
#添加后台的搜索栏
search_fields = ('title', 'body')
#实现slug字段跟随title的输入自动填充
prepopulated_fields = {'slug': ('title', )}
#暂时不懂???
raw_id_fields = ('author',)
#在后台增加publish的日期显示
date_hierarchy = 'publish'
#按照publish和status字段排序记录
ordering = ['publish','status']
admin.site.register(Post,PostAdmin)
四 3,使用视图函数和模板将数据库内容展现在前端界面:(使用视图函数3步【求快可2步】+使用模板3步【其实可以2步】)
总体思路通俗版:
a、浏览器通过用户request【客户点餐】
b、django通过url.py文件找到人(view)去处理【酒店找到对应厨师做菜】c、view.py处理玩通过render返回数据response给用户【厨师做完菜让服务员打包好送给客户】
总体思路专业版:
1、用户在浏览器点击根据html文件上的链接;(涉及xxx.html文件)
2、点击链接之后,django中的url.py文件将链接对应到对应的view.py文件中的视图函数(涉及url.py文件)
3、view.py内的视图函数读取数据库,并通过render()函数,将数据反馈给xxx.html并传给浏览器;
使用视图函数3步【求快可2步】:
步骤一:rango/views.py 写视图函数如index();
def index(request):
# Query the database for a list of ALL categories currently stored.
# Order the categories by no. likes in descending order.
# Retrieve the top 5 only - or all if less than 5.
# Place the list in our context_dict dictionary which will be passed to the template engine.
category_list = Category.objects.order_by('-likes')[:5]
context_dict = {'categories': category_list}
# Render the response and send it back!
return render(request, 'rango/index.html', context_dict)
这里djangobyexample中的view.py代码有个bug,还没解决:
from django.shortcuts import render,get_object_or_404
from blog.models import Post
# Create your views here.
def post_list(request):
posts = Post.published.all()
context_dict = {'posts':posts}
return render(request,'blog/post/list.html',context_dict)
def post_detail(request, year, month, day, post):
post = get_object_or_404(Post, slug=post,
status='published',
publish__year=year,
#这里是个bug,用月日筛选会发现找不到,这里的月传入的是英文,而数据库的是数字,造成不匹配
#publish__month=month,
#publish__day=day
)
return render(request, 'blog/post/detail.html', {'post': post})
步骤二:rango/urls.py 添加url()函数,使视图函数与url正则表达式链接,其中的name参数可以是模板使用这个参数标签来引用对应的url;
urlpatterns = [url(r'^$', 'views.index', name='index'),]
步骤三:tango_with_django/urls.py 添加 urls(r’rango/’,include(‘rango.urls’));目的是使应用的urls被项目识别,其实也可以直接在项目文件中直接写url,之所以不这样做是为了解耦合。
使用模板3步【其实可以2步】:
settings的设置参考官方文档:https://docs.djangoproject.com/en/1.8/ref/settings/#template-dirs
关于模板的settings这里更详细:https://docs.djangoproject.com/en/1.8/topics/templates/
比如:
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',#驱动,django自带,可以改成自己喜欢的引擎,比如flask中的jin2
'DIRS': [],#项目模板存放的位置,需要自己写,具体见下图配置
'APP_DIRS': True,#APP模板的位置,设置为true则django会自动去加载APP下的名为templates的文件夹,并渲染,这样比较省事
'OPTIONS': {#文件渲染的自带的处理器,使用render_to_response()渲染函数时需要用到;
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
first在工程下建立模板目录:
templates/rango,注意这里是在工程目录的第一级目录建立templates目录,不是第二级,见second图;
second 在settings中设置链接建立的模板目录:
settings>>TEMPLATES >>DIRS中添加TEMPLATE_PATH,其中TEMPLATE_PATH=os.path.join(BASE_DIR,’templates’)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(file)))
ps:BASE_DIR 其实django自己就写好了
这里解决了两个问题,os.path.join 解决了不同操作系统的路径名的不同分隔符的问题,os.path.dirname解决了硬编码问题,即建议使用相对路径,不用绝对路径,否则程序不便移植。
图片参考djangobyexample项目:
PS:其实这里不用这么麻烦,直接在APP下即blog/下新建一个templates目录,django就会自动去找,不用设置DIRS,这里就是提供一个如果想自己设置templates文件夹目录的思路
third ,在模板目录templates/rango下添加html文件,注意{{a}}是可以代表视图函数传来的变量;
PS:这里比较下
def my_view(request):
# View code here...
return render(request, 'myapp/index.html', {
'foo': 'bar',})
与
return render_to_response('my_template.html', local(), context_instance=RequestContext(request))
的区别:
render_to_response()更方便,因为它与render()相比,包装了更多的参数,就是context_instance=RequestContext(request)的加入,这个参数引用的就是settings.py中的template中的’OPTIONS’的’context_processors’,这样你就不用自己在每个视图函数中重复写一些常用的参数,而render中只是包装了request,所以很多需要的参数要自己写,比较麻烦,其中的local()是python中的语法,可以代替所有的字典参数,非常方便,比如render上面的{‘foo’: ‘bar’,},所以建议开发中使用render_to_response()加local()函数;
PPS:囧,在1.10版本中官方取消了render_to_response()这个函数,并建议使用render()函数,我估摸着,django以后可能自动将其他参数包装放到render了。所以还是老老实实的用render吧~
5,使用静态文件,包括图片,js,css(共四步):
first:在工程目录下设置目录并放置图片,比如:static;可以在static下再设置一个目录便于区分,即static/image/rango.jpeg
second:在settings文件里告知目录位置,
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR,'static')
STATIC_PATH = os.path.join(BASE_DIR,'static') #非必须
STATICFILES_DIRS = ( #非必须
STATIC_PATH,
)
这里讲一下三个参数的区别(STATIC_PATH归到STATICFILES_DIRS中),首先URL与ROOT是必须参数,URL是负责供给模板html使用,如果是本地环境,其实代表127.0.0.1:5000/static;
ROOT是django将查找静态文件的功能交给服务器之后,服务器要查找的静态文件的路径,这里URL其实就是指向ROOT,在部署阶段,只要运行命令:
python manage.py collectstatic #先搜索再复制
django就自动搜索每个应用目录下的static文件夹里的文件,以及STATICFILES_DIRS设置的目录的文件,将所有文件复制到ROOT所指定的目录下,以便供给服务器去查找,所以这里可以看出STATICFILES_DIRS不是必须的,当然如果你如果将静态文件放在除应用目录下的static与工程目录下的static之外的其他目录,那就需要设置。
总结一下:STATICFILES_DIRS是各种源静态文件目录;
STATIC_ROOT 是目的静态文件目录,是将源静态文件集中到了一起,方便使用;
STATIC_URL 是html去使用静态文件的目录,指向STATIC_ROOT;
third:在项目里的urls利用setting里面的设置:
if not settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
if settings.DEBUG:
urlpatterns += [
url(r'^static/(?P<path>.*)$', views.static.serve, {'document_root': settings.STATIC_ROOT}, name="static"),]
forth:在html中使用静态文件:
{% load staticfiles %}
< img src = "{% static " images/rango.jpeg" %}" />
html中的load staticfiles其实指的就是django.contrib.staticfiles ,而html中static指的就是127.0.0.1:8000/static/(如果是本地情况下),其实static标签就是url中名为name=’static’的url;。
参考链接:http://m.oschina.net/blog/198768
6,静态媒体服务(大致五步):
first:在工程目录下新建media目录
second:setting设置路径:
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
third:url使用设置的路径:
url(r'^media/(?P<path>.*)$', views.static.serve, {'document_root': settings.MEDIA_ROOT}, name="media")
forth:在model数据库模型中设置相应字段:
picture = models.ImageField(upload_to='profile_images', blank=True)
ImageField字段有一个upload_to属性.这个属性值连接着MEDIA_ROOT设置用来提供上传文档图片的路径.例如,MEDIA_ROOT设置为/tango_with_django_project/media/,那么upload_to=profile_imges将会使图片保存在/tango_with_django_project/media/profile_images/目录.
fifth:html文件里的设置:
<form id="user_form" method="post" action="/rango/register/"
enctype="multipart/form-data">
当你希望用户通过表单上传文件时,必须把enctype设置成multipart/form-data.这个属性会让你的浏览器以特定的方式把表单数据返回给服务器.
8,将数据库的内容显示在html上(3步):
first:假设已完成7,即确保数据库中已经有数据;
second:在视图函数中导入数据库模型;实例化(c=Catotery.objects.all());将实例当做字典用render()传给前端模板。
third:在前端模板中用变量表示出来。作为模板语言,所有的命令都包含在{%和%}标签里,所有的变量都在{{和}}里.
9,在Category添加slug字段:
first:重写Category模型的save方法,我们将会调用slugify方法并更新slug字段:
from django.template.defaultfilters import slugify
class Category(models.Model):
# 。。。。
slug = models.SlugField(unique=True)
def save(self, *args, **kwargs):
self.slug = slugify(self.name)
super(Category, self).save(*args, **kwargs)
second:视图函数中添加catetory函数:
def category(request, category_name_slug):
#。。。
category = Category.objects.get(slug=category_name_slug)
third:添加category.html,并修改index模板:
<li><a href="/rango/category/{{ category.slug }}">{{ category.name }}</a></li>
forth:修改rango的urls.py文件和urlpatterns元组
url(r'^category/(?P<category_name_slug>[\w\-]+)/$', views.category, name='category'),
10,使用表单,增加目录条目使用(4加1步):
first:创建表单类,django的表单类是直接跟数据库模型直接相关,在rango/forms.py中:
class CategoryForm(forms.ModelForm):
name = forms.CharField(max_length=128, help_text="Please enter the category name.")
views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
likes = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
slug = forms.CharField(widget=forms.HiddenInput(), required=False)
class Meta:
model = Category
fields = ('name',)
class PageForm(forms.ModelForm):
title = forms.CharField(max_length=128, help_text="Please enter the title of the page.")
url = forms.URLField(max_length=200, help_text="Please enter the URL of the page.")
views = forms.IntegerField(widget=forms.HiddenInput(), initial=0)
class Meta:
model = Page
exclude = ('category',)
second:视图函数rango/views.py:
def add_category(request):
if request.method == 'POST':
form = CategoryForm(request.POST)
if form.is_valid():
form.save(commit=True)
return index(request)
else:
print form.errors
else:
form = CategoryForm()
return render(request, 'rango/add_category.html', {'form': form})
third:表格模板html的编写,templates/rango/add_category.html:
<form id="category_form" method="post" action="/rango/add_category/">
{% csrf_token %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% for field in form.visible_fields %}
{{ field.errors }}
{{ field.help_text }}
{{ field }}
{% endfor %}
<input type="submit" name="submit" value="Create Category" />
</form>
链接模板html的编写,rango/index.html:
<a href="/rango/add_category/">Add a New Category</a><br />
forth:映射url,rango/urls.py:
url(r'^add_category/$', views.add_category, name='add_category'),
fifth:设想有时候用户输入并不是一定正确,我们可以重写ModelForm模块里clean()方法.这个方法会在表单数据存储到模型实例之前被调用,所以它可以让我们验证甚至修改用户输入的数据.在我们上面的例子中,我们可以检查url字段的值是否以http://开头 - 如果不是我们可以在用户前面添加上http://
class PageForm(forms.ModelForm):
...
def clean(self):
cleaned_data = self.cleaned_data
url = cleaned_data.get('url')
if url and not url.startswith('http://'):
url = 'http://' + url
cleaned_data['url'] = url
return cleaned_data
11,用户验证(登陆,登出,注册等):
两种方法,一种自己写;第二种django提供了一个插件django-registration-redux,安装这个包,可以不用写模型,直接应用,但是也要写前端,前端的描述也变得简单,详见:http://hackerxu.com/Twd/#12-drr
12,使用bootstrap模板写前端:
大体思路是在基模板中引入bootstrap的css与js文件,之后其他前端模板只要继承基模板就好了,所以基模板比较复杂,其他还好,base.html中,添加css与js文件,这样就可以用bootstrap了,这跟django没关系,前端要用bootstrap都可以这么用:
<head>
...
<meta name="viewport" content="width=device-width, initial-scale=1">
...
<link href="http://getbootstrap.com/dist/css/bootstrap.min.css" rel="stylesheet">
...
</head>
<body>
...
<script src="http://getbootstrap.com/dist/js/bootstrap.min.js"></script>
...
</body>