Django是现在世界上python语言应用最广泛、发展最成熟的web框架。它足够完整 ,不需要依赖其他就可以开发出 非常完整的web应用。Django的主要内容如下:
Django于2003年诞生于美国的堪萨斯州,最初是用来制作在线新闻web网站,于2005年加入了BSD许可证家族,成为开源网络框架。Django根据比利时的爵士音乐家Django Reinhardt命名。
相对于python的其它web框架,Django的功能是最完整的,Django定义了服务发布,路由映射,模板编程,数据处理的一整套功能。这也意味着Django模块之间紧密耦合,开发者需要学习Django自己定义的一整套技术。它的主要特点如下:
Django是遵循MVC架构的web开发框架,主要又以下几个部分组成。
在安装pip工具的python环境中可以直接使用pip install django命令进行安装。或者下载Django的离线包,使用python setup.py install命令进行安装。
安装完成后,进入python,通过如下命令测试是否安装成功。
import django
print(django.version)
在进行项目开发的第一步就是要建立Django项目,建立的语法如下:
django-admin startproject djangotest。
上述代码中的django-admin是安装好Django组件后在python目录中生成的Django项目管理工具。该命令在当前的目录下创建了项目工程文件,工程文件中的目录结构如下:
上图就是成功生成的项目文件,默认生成的几个文件非常重要,在今后的开发或者维护中要一直使用它们。各个文件简单的解释如下:
为了在项目中开发符合MVC架构的实际应用程序,我们需要在项目中建立Django应用。每一个django项目可以包含多个django应用。应用的建立语法如下。
python manage.py startapp app。
其中的manage.py是建立项目是用到的命令行工具,startapp是命令行关键字,app是建立的应用名称。命令完成后会在项目的目录下建立如下图所示的文件结构。
其中各个文件功能的说明如下:
在完成django项目和应用的建立后,就可以开始编写网站的代码了,下面简单演示一下django的路由映射功能。
(1)在app/views.py中创建一个路由响应函数,代码如下
from django.http import HttpResponse def welcome(request): return HttpResponse(‘<h1>你好,django</h1>‘)
(2)接下来,要通过URL映射将用户的HTTP访问和该函数绑定起来,在app/目录下创建一个urls.py文件,其文件的内容如下:
from django.contrib import adminfrom django.conf.urls import url from .apps import views urlpatterns = [ url(r‘‘,views.welcome), #url(r‘‘,views.welcome), ]
(3)在项目djangotest/urls.py文件中的urlpatterns中新增加一项,声明对应用app中的urls.py文件的引用,具体代码如下
from django.contrib import admin from django.urls import url from django.conf.urls import include urlpatterns = [ url(‘admin/‘, admin.site.urls),
url(r‘app/‘, include(‘app.urls‘)),
]
首先导入django.conf.urls.include()函数,之后再urlpatterns列表中增加一个路径app/,将其转接到app.urls包,这样通过include()函数就可以将2个urlpatterns连接起来。
(4)上述代码书写完成后,输入python manage.py runserver 127.0.0.0:8888。命令开启web服务,在浏览其中输入网址http://127.0.0.0:8888/app,结果如下图所示。
这一节介绍的是Model层的处理,设计和开发信息发布的数据访问层。
要在django项目的setting.py文件中告诉django需要安装应用app中的模型,方法是在setting.py文件中的INSTALLED_APPS数据结构中,在其中添加应用app的 config类,代码如下:
INSTALLED_APPS = [ ‘app‘, # 新增 ]
打开app/models.py文件,在其中新建一个模型类Mount,用来定义信息发布表,代码如下:
from django.db import models class Mount(models.Model): content = models.CharField(max_length=200) username = models.CharField(max_length=20) kind = models.CharField(max_length=20)
首先引入modoels类,所有的django模型类都是继承于它。类Mount继承了models类,在其中定义了3个字段content:消息的内容、username:发布人名称、kind:消息的类型。
django的术语“生成数据移植文件”是指将models.py中定义的数据表转换成数据库生成脚本的过程。该过程通过命令行工具manage.py完成,具体的命令输出如下:
通过输出我们可以看到完成了模型Mount的建立。输出中的0001_initial.py是数据库生成的中间文件,通过它可以知道当前数据库的版本;该文件以及以后所有的migration文件都存储于目录migrations/中。
同时在makemigrations的过程中,django会对比models.py中模型和已有数据库之间的差异,没有差异则不做操作,如再次执行makemigrations操作时将产生如下输出:
但是如果对models.py文件作出修改时,则在下一次makemigrations的时候会将修改的内容同步到数据库中。例如将kind字段修改为长度30后,执行操作,结果如下:
在该过程中产生了新的中间文件0002_auto_20190526_1431.py。需要注意的是djangotest\app\migrations目录中的文件都有manage.py自己维护,开发者不宜修改其中的内容。
在模型的修改过程中可以随时调用makemigrations生成的中间移植文件。当需要真正的修改数据库的时候,需要通过manage.py的migrate命令让修改同步到数据库。例如:
建立表单类文件djangotest/app/forms.py,在其中定义表单类MountForm,代码如下:
from django.forms import ModelForm from models import Mount class MountForm(ModelForm): class Meta: model = Mount fields = ‘__all__‘
为了使用户能够以单选的方式设置消息类型,则需要在models.py文件中定义单选的枚举类型,并且与模型类Mount进行关联。代码如下:
from django.db import models # Create your models here. class Mount(models.Model): content = models.CharField(max_length=200) username = models.CharField(max_length=20,default=‘张晓琳‘) # 新增元祖用于设置消息枚举类型 KIND_CHOICES = ((‘python‘,‘python‘),(‘java‘,‘java‘),(‘C语言‘,‘C语言‘), (‘C++‘, ‘C++‘), (‘mysql‘, ‘mysql‘),) kind = models.CharField(max_length=30,choices=KIND_CHOICES,default=KIND_CHOICES[0])
模板文件是python web框架中用于产生HTML、XML等文本格式文档的术语。模板文件本身也是一种文本文件,开发者需要手工进行编辑。建立目录djangotest/app/templates,在其中新建模板文件moments_input.html,文件内容如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>消息录入页面</title> </head> <body> <form action="?" method="post"> <fieldset> <legend>请输入并提交</legend> {{ form.as_p }} <input type="submit" value="提交" /> </fieldset> </form> </body> </html>
模板文件是html文件,其中的模板内容使用大括号进行标识。这里使用{{ form.as_p }}定义表单类的输入字段。
接下来开发的是视图文件,使得表单类和页面模板能够衔接起来。打开djangotest/app/views.py文件,在其中加入如下函数:
import os
from app.form import MountForm
from django.http import HttpResponseRedirect
from django.shortcuts import render
from django.http import HttpResponse
# Create your views here.
def moments_input(request):
if request.method == ‘POST‘:
form = MountForm(request.POST)
if form.is_valid():
moment = form.save()
moment.save()
# return HttpResponseRedirect("app.views.welcome")
else:
form = MountForm()
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
return render(request,os.path.join(project_root,‘app/templates‘),‘moments_input.html‘,{‘form‘:form})
在urls.py文件中增加视图函数的路由映射。
urlpatterns = [
url(‘admin/‘, admin.site.urls),
url(r‘^app/‘, include(‘app.urls‘)),
]
访问地址为http://127.0.0.1:9999/app/moments_input。
站点分为“内容发布”和“公共访问”两部分,“内容发布”的部分负责添加、修改、删除内容,开发这些重复的功能是一件单调乏味、缺乏创造力的工作。为此,Django会根据定义的模型类完全自动地生成管理模块
python manage.py createsuperuser
,按提示输入用户名、邮箱、密码
编辑settings.py文件,设置编码、时区
LANGUAGE_CODE = ‘zh-Hans‘ TIME_ZONE = ‘Asia/Shanghai‘
打开booktest/admin.py文件,注册模型
from django.contrib import admin from models import BookInfo admin.site.register(BookInfo)
刷新管理页面,可以对BookInfo的数据进行增删改查操作
问题:如果在str方法中返回中文,在修改和添加时会报ascii的错误
解决:在str()方法中,将字符串末尾添加“.encode(‘utf-8‘)”
Django提供了admin.ModelAdmin类
通过定义ModelAdmin的子类,来定义模型在Admin界面的显示方式
class QuestionAdmin(admin.ModelAdmin): ... admin.site.register(Question, QuestionAdmin)
list_display = [‘pk‘, ‘btitle‘, ‘bpub_date‘]
list_filter = [‘btitle‘]
search_fields = [‘btitle‘]
list_per_page = 10
fields = [‘bpub_date‘, ‘btitle‘]
fieldsets = [ (‘basic‘,{‘fields‘: [‘btitle‘]}), (‘more‘, {‘fields‘: [‘bpub_date‘]}), ]
对于HeroInfo模型类,有两种注册方式
按照BookInfor的注册方式完成HeroInfo的注册
接下来实现关联注册
from django.contrib import admin from models import BookInfo,HeroInfo class HeroInfoInline(admin.StackedInline): model = HeroInfo extra = 2 class BookInfoAdmin(admin.ModelAdmin): inlines = [HeroInfoInline] admin.site.register(BookInfo, BookInfoAdmin) 可以将内嵌的方式改为表格 class HeroInfoInline(admin.TabularInline)
发布性别的显示不是一个直观的结果,可以使用方法进行封装
def gender(self): if self.hgender: return ‘男‘ else: return ‘女‘ gender.short_description = ‘性别‘ 在admin注册中使用gender代替hgender class HeroInfoAdmin(admin.ModelAdmin): list_display = [‘id‘, ‘hname‘, ‘gender‘, ‘hcontent‘]
Django模型层是Django框架自定义的一套独特的ORM技术,通过上几章节的总结学习,我们已经大概了解了Django模型层的基本概念和开发流程。现在来学习模型层的技术细节和高级话题。
使用Django模型开发的首要任务就是定义模型类和类的属性。每个模型类都可以被映射为数据库中的一个表,类属性被映射成为数据字段,除此之外,数据库中的主键、外键、约束等也可以通过类属性完成定义。
模型定义的的基本结构如下所示:
from django.db import models class ModelsName(models.Model): field1 = models.XXfield(...) field2 = models.XXfield(...) ... class Meta: db_table = ... other_metas = ...
属性 |
属性值 |
说明 |
abstract |
True or False |
标识本类是否为抽象的基类 |
app_lable |
app_lable = “myapp” |
定义本类所属的应用 |
db_table |
db_table=“mytable” |
映射的数据库表名称 说明:如果Meta中不提供db_table字段,则django会为模型自动生成数据库表名,格式为“应用名_模型名”。 |
db_tablespace |
无 |
映射的表空间名称。表空间的概念只在某些数据库中如oracle中存在,不存在表空间概念的数据库将会忽略本字段。 |
Default_related_name |
无 |
定义本模型的反向关系引用名称,默认和模型名一致。 |
get_latest_by |
无 |
定义按那个字段值排列获得模型的开始或者结束记录,本属性值通常指向一个日期或者整形的模型字段。 |
managed |
True or False |
定义django的manage.py命令行工具是否管理本模型。属性默认设置为TRUE。设置为false时,需要手动维护数据库表。 |
order_with_respect_to |
无 |
定义本模型可以按照某种外键引用的关系排序。 |
ordering |
Ordering = [“username”“-pub_data”] |
本模型记录的默认排序字段,可以设置多个字段,默认为升序排列。降序排列的话在字典前加上- |
default_permissions |
Add,change,delete |
模型的操作权限,默认如值所示。 |
proxy |
True or False |
本模型及所有继承自本模型的子类是否为代理模式。 |
required_db_features |
fequired_db_features =[“gis_enabled”] |
定义底层数据库所必须具备的特性,如值所示只将本数据模型生成在满足gis_enabled特性的数据库中 |
required_db_vendor |
SQLite、PostgreSQL、MySQL、Oracle |
定义底层数据库的类型,如果定义了本属性,则模型只能在其声明的数据库中维护。 |
unique_together |
unique_together = ((“user”,“pwd”)) |
用来设置不重复的的字段组合,必须唯一(可以将多个字段做联合唯一)。 |
index_together |
index_together=[ [“pun_data”,“deadline”]] |
定义联合索引的字段,可以定义多个。 |
verbose_name |
verbose_name = ‘资产管理’ |
定义一个易于理解和表述的单数形式的对象名称。如果不设置该值,则Django将会使用该models的类名作为它的对象表述名。 |
verbose_name_plural |
|
定义一个易于理解和表述的复数形式的对象名称。 |
Django的普通字段是指模型类中除了外键以外的数据字段属性。它为Django使用模型的时候提供如下信息:
所有的数据字段的属性必须继承自抽象类django.db.models.Field,我们可以自己定义继承自该类的数据字段,也可以使用Django预定义的一系列数据字段,常见的Django预定义字段类型描述如下表所示。
字段 |
字段说明 |
AutoField |
一个自动递增的整形字段,添加记录时它会自动增长。AutoField字段通常充当数据表的主键;如果没有添加,django会自动添加一个该字段。 |
BigIntegerField |
64位的整型字段 |
BinaryField |
二进制数据字段,通过bytes进行赋值 |
BooleanField |
布尔字段,对应<input type=“CheckBox”> |
CharField |
字符串字段,对应<input type=“text”> |
TextField |
大容量文本字段,对应<textarea> |
CommaSeparatedIntegerField |
存放逗号分隔的整数值,有特殊的表单验证要求 |
DateField |
日期字段,相对应的HTML标签是<input type=“text”>、JavaScript日历和一个today快捷按键。可选参数:auto_now:保存时,设置为当前时间;auto_now_add:首次创建时,保存为当前时间。 |
DateTimeField |
类似于DateField,但是它却支持时间的输入。 |
DurationField |
存储时间周期,用python的timedelta类型构建。 |
EmailField |
检查Email合法性的CharField。 |
FileField |
文件上传字段。在定义本字段是必须传入参数upload_to,用于保存上传下载文件的服务器文件系统的路径。这个路径必须包含strftime formatting,该格式将被上载文件的date/time替换 |
FilePathField |
按照目录的规则限制选择文件,定义本字段是必须传入参数path,用以限定目录。 |
FloatField |
浮点型字段。定义本字段时必须传入参数max_digits和decimal_places,用于定义总位数和小数位数。 |
ImageField |
类似于 FileField,同时验证上传的对象是否是一个合法图片,可选参数有2个:height_field和width_field,有这两个参数,图片按照规定的长和高进行存储。 |
IntegerField |
保存一个整数 |
IPAddressField |
一个字符串形式的IP地址 |
NullBooleanField |
类似于BooleanField,但是多出了一个None选项 |
PhoneNumberField |
带有美国风格的电话号码校验的CharField(格式为XXX-XXX-XXXX) |
PositiveIntegerField |
只能输入非负数的IntegerField |
SlugField |
只包含字母,数字,下划线和连字符的输入,通常同于URL |
SmallIntegerField |
类似于IntegerField,但是只有较小的输入范围,依赖所使用的数据库。 |
TimeField |
时间字段,类似于DateTimeField,但只能表达和输入时间。 |
URLField |
用于保存URL |
USStateField |
美国州名的缩写字段 |
XMLField |
XML字符字段,是具有XML合法性验证的TextField |
|
|
每个字段类型都有一些特定的HTML标签和表单验证参数,比如heigth_field,path等,但同时每个字段都有可以设置的公告方法,比如primary_key参数设置的主键字段。
from django.db import models class Comment(models.Model): # 设置主键id id = models.AutoField(primary_key=True)
from django.db import models class Comment(models.Model): # 设置主键id id = models.AutoField(primary_key=True) # 定义选择元祖 levels = ((‘1‘,‘Very Good‘),(‘2‘,‘Good‘),(‘3‘,‘Normal‘),(‘4‘,‘Bad‘)) level = models.CharField(max_length=10,choices=levels)
上述代码中定义了level字段用于让用户选择满意度,其中的1,2,3,4是在数据库中实际存储的数据,而very good、Good、Normal、Bad等是在HTML页面中供选择的选项。
除了这些名称的字段外,Django中的所有字段还有一个无名参数,可以设置该字段在HTML页面的显示名称,比如:
level = models.CharField(‘客户满意度选择‘,max_length=10,choices=levels)
定义如下的Django models,用于演示Django的模型基本查询技术。
from django.db import models class Comment(models.Model): # 设置主键id id = models.AutoField(primary_key=True) # 定义选择元祖 levels = ((‘1‘,‘Very Good‘),(‘2‘,‘Good‘),(‘3‘,‘Normal‘),(‘4‘,‘Bad‘)) level = models.CharField(‘客户满意度选择‘,max_length=10,choices=levels) headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateTimeField() n_visits = models.IntegerField() def __str__(self): return self.headline
(1) Django有2中过滤器用于对记录进行刷选,如下:Django通过模型的objects对象查询模型的数据,例如下面的语句查询Comment模型的所有数据:Comment.objects.all()
(2)连接查询
多个filter和exclude可以连在一起进行查询,比如查询所有2015年非1月的n_visits不为0的记录:Comment.objects.filter(pub_date_year == 2015).exclude(pub_date_month=1).exclude(n_visits_exact=0)。
我们可以看到代码中的pub_date_year,它并不是模型中定义的一个字段,而是Django定义的一种独特的字段查询方式,本例中该查询的含义是“pub_date字段的year属性为2015”。基本的表现形式为 字段名称_谓词,既由“双下划线连接的字段名称和谓词”来表达查询条件。类似的还有很多其他的谓词查询方式,完整的Django谓词列表如下标所示。
谓词 |
含义 |
示例 |
等价的sql语句 |
exact |
精确等于 |
Com.objects.filter(id_exact=14) |
Select * from Com where id = 14; |
iexact |
大小写不敏感的等于 |
Com.objects.filter(headline_iexact=”I am ok”) |
Select * from Com where upper(headline)=” I am ok” |
contains |
模糊匹配 |
Com.objects.filter(headline_ contains =”good”) |
Select * from Com where headline like ”%good%”; |
in |
包含 |
Com.objects.filter(id_ in =[1,5,9]) |
Select * from Com where id in [1,5,9]; |
gt |
大于 |
Com.objects.filter(id_ gt=30) |
Select * from Com where id > 30; |
gte |
大于等于 |
||
lt |
小于 |
||
lte |
小于等于 |
||
startwith |
以..开头 |
Com.objects.filter(body_text_ startwith=”wl”) text_ endwith=”wl” |
Select * from Com where body_text like ”good%” ”%good”; |
endwith |
以..结尾 |
||
range |
在..范围内 |
Com.objects.filter(pub_date_range=(starttime,endtime)) |
Select * from Com where pub_date between starttime and endtime; |
year |
年 |
Com.objects.filter(pub_date_year == 2015) |
Select * from Com where pub_date between “2015-1-1 00:00:00” and “2015-12-31 23:59:59”; |
month |
月 |
||
day |
日 |
||
Week_day |
星期几 |
||
isnull |
是否为空 |
Com.objects.filter(pub_date_isnull = True) |
Select * from Com where pub_date is Null; |
(3)除了all()、filter()、exclude()等返回数据集的函数,Django还提供了get()函数用于查询单挑记录,比如查询id为3的记录:
Com.objects.get(id_exact = 1)
(4)Django还提供了用于查询指定条数的小标操作,该特性使得Django模型能够支持SQL中的limit和offset。
(5)Django还提供了order_by操作,示例如下:
Com.objects.order_by(“headline”):返回数据集,按照headline字段排序。
和传统的sql相比,Django的一个较大的优势是定义了一个统一的方法save(),用于完成模型的insert和update操作。在执行模型的save()函数时,Django会根据模型的主键,判断记录是否存在,如果存在执行update操作,不存在执行insert操作。
# 新增记录 obj = Comment(headline=‘i am ok‘,body_text=‘...sdjk‘, pub_date = datetime.datetime().now(),n_visits=0) obj.save() print(obj.id) # 打印主键id22 # 修改记录数据 obj.body_text = "this is my world, I am god!!" obj.save() print(obj.id) # 打印主键id=22,和新增后的id相同。
4.2.3 数据删除
Django模型提供了delete()方法用于删除记录,该方法可以进行单条和批量删除。示例代码如下:
# 删除id为3的记录 Comment.objects.get(id=3).delete() # 删除2015年所有的记录 Comment.objects.filter(pub_date_year = 2015).delete()
利用数据表之间的关系进行数据建模和业务开发是关系型数据库最主要的功能。Django的模型层对这三种关系模型(1:1,1:N;M:N)都有强大的支持。
在SQL语言中,一对一的关系通过在两个表之间定义相同的主键来完成。在Django的模型层,可以在任意一个模型中定义OneToOneField字段,并且定义相互之间的一对一关系。如下的代码在模型Account和Contact之间定义了一对一的关系。
class Account(models.Model): user_name = models.CharField(max_length=80) password = models.CharField(max_length=255) reg_date = models.DateTimeField() def __str__(self): return "Account:%s" % self.user_name class Contact(models.Model): account = models.OneToOneField(Account,on_delete=models.CASCADE,primary_key=True) zip_code = models.CharField(max_length=10) address = models.CharField(max_length=80) mobile = models.CharField(max_length=20) def __str__(self): return "%s,%s" % (self.account.user_name,self.mobile)
在上述一对一的关系模型中的开发代码如下所示。
a1 = Account(user_name=‘wltest‘) a1.save() # 保存一个Account记录 print(a1) # 打印a1:<Account:wltest> a2 = Account(user_name=‘zxlText‘) a2.save() # 保存一个Account记录 # 利用a1初始化Contact的account字段,并保存 a3 = Contact(account=a1,mobile=‘1223123123‘) a3.save() print(a3) # <Contact:wltest,1223123123> print(a1.contact) # <Contact:wltest,1223123123>,通过关系打印,于打印a3的结果相同 print(a3.account) # <account:wltest>,通过关系打印,于打印a1的结果相同 # a2没有和Contact建立过关系,所以没有contact字段。 a1.delete() # on_delete = models.CASCADE,所以删除a1时也删除了a3
在SQL语言中,1:N关系通过在“附表”中设置“主表”的外键引用来完成。在Django模型层,可以使用models.ForeignKey()来定义外键,代码如下:
from django.db import models class Account(models.Model): user_name = models.CharField(max_length=80) password = models.CharField(max_length=255) reg_date = models.DateTimeField() def __str__(self): return "Account:%s" % self.user_name class Contact(models.Model): # account = models.OneToOneField(Account,on_delete=models.CASCADE,primary_key=True) account = models.ForeignKey(Account, on_delete=models.CASCADE) zip_code = models.CharField(max_length=10) address = models.CharField(max_length=80) mobile = models.CharField(max_length=20) def __str__(self): return "%s,%s" % (self.account.user_name,self.mobile)
上述代码于一对一关系唯一不同的地方在于models.ForeignKey定义了Contact模型中的account字段。这样一来,每个Account对象就可以与多个Contact对象关联了。模型的使用代码如下:
def welcome(request): a1 = Account(user_name=‘wl2‘,password=‘12343‘) a1.save() # 保存一条Account记录 # 为a1建立两个Contact关联的对象 c1 = Contact(account=a1,address=‘c1‘,mobile=‘12312321412‘) c1.save() # 保存C1记录 c2 = Contact(account=a1, address=‘c2‘, mobile=‘12312321412‘) c2.save() # 保存C2记录 data = {c1.address:str(c1.account), c2.address:str(c2.account),‘a1‘:str([a1.contact_set,a1.contact_set.count()])} a1.delete() return JsonResponse({‘status‘:200,‘data‘:data})
在一对多关系中,每个主模型对象可以关联多个子对象,所以本例中从主模型Account对象寻找附属模型Contact的属性时contact_set,通过一个集合返回关联结果。
说明:XXX_set是Django设定的通过主模型对象访问附属模型的对象集合的属性名。
在SQL语言中,M:N关系通过建立一个中间关系表来实现,该中间表中定义了到2个主表的外键。所以在Django模型层中,开发者也可以选择用这两个1:N关系来定义M:N的关系。这种方式是通过models.ForeignKey来实现。
另外一种方式是在django的模型层中定义一个models.ManyToManyField字段来实现的,多对多关系的模型定义代码如下:
from django.db import models class Account(models.Model): user_name = models.CharField(max_length=80) password = models.CharField(max_length=255) # reg_date = models.DateTimeField() def __str__(self): return "Account:%s" % self.user_name class Contact(models.Model): account = models.ManyToManyField(Account) zip_code = models.CharField(max_length=10) address = models.CharField(max_length=80) mobile = models.CharField(max_length=20) def __str__(self): return "%s,%s" % (self.account.user_name,self.mobile)
上述代码通过在Contact中定义引用Account的ManyToManyField,实现了2个模型的多对多关联,对此模型的定义操作演示如下:
# 分别建立并保存Account和Contact的对象 a1 = Account(user_name=‘ww‘) a1.save() c1 = Contact(mobile=‘weeee3‘) c1.save() # t通过Contact对象建立关系 c1.account.add(a1) a2 = Account(user_name=‘jjj‘) a2.save() a2.contact_set.add(c1) # 通过Account对象建立关系 a3 = Account(user_name=‘jjj‘) # 对象未保存出错 a3.contact_set.add(c1) # 通过Account对象建立关系 a1.contact_set.remove() # 取消单个对象的关联 a1.contact_set.clear() # 取消所有对象的关联
Django模型层ORM的一个强大之处是对模型继承的支持,该技术将python面向对象的编程方法与数据库面向关系表的数据有机的结合。支持三种风格的模型集成。相面来介绍这三种继承方法。
抽象类继承是指父类继承models.Model,但不会在底层的数据库中生成相应的数据表,父类的相关属性字段存储在子类的数据表中。
它的作用是在多个表有想干的字段时,可以使得开发者将这些字段统一定义在抽象基类中,免于重复定义这些字段。抽象基类的定义通过在模型的Meta中定义属性abstract=True来实现,示例代码如下:
class MessageBase(models.Model): id = models.AutoField() content = models.CharField(max_length=100) user = models.CharField(max_length=20) pub_date = models.DateTimeField() class Meta: # 定义本类为抽象基类 abstract = True class Mon(MessageBase): headline = models.CharField(max_length=50) class Com(MessageBase): life = models.CharField(max_length=30,default=‘wl‘,choices=((‘1‘,‘2‘),(‘sjdk‘,‘idi‘)))
在子类模型的编程中,可以直接使用父类定义的字段,例如:上述代码中定义了一个抽象基类MessageBase,用于保存消息的4个字段。子类Mom和Com继承自MessageBase,并分别定义了自己的一个字段。这3个类映射到数据后,会别定义成2个数据库表。每个表都有5个字段
m1 = Mon(user=‘ds‘,headline=‘sds‘) m1.content = ‘sddhjk‘ m1.save()
多表继承的模型类都在底层的数据库中生成了相应的数据表管理数据。无论是父表还是子表都会用数据库中相对应的数据表维护模型的数据,父类中的字段不会重复的在多个子表中进行定义。从这方面来讲,多表继承才是真正面向对象的ORM技术。
多表继承不需要特殊的关键字。在Django内部通过在父类和子类之间建立一对一关系来实现多表继承,示例代码如下。
from django.db import models class MessageBase(models.Model): id = models.AutoField() content = models.CharField(max_length=100) user = models.CharField(max_length=20) pub_date = models.DateTimeField() class Mon(MessageBase): headline = models.CharField(max_length=50) class Com(MessageBase): life = models.CharField(max_length=30,default=‘wl‘,choices=((‘1‘,‘2‘),(‘sjdk‘,‘idi‘)))
仍然可以直接引用父类定义的字段,同时子类可以通过父类对象引用访问父类的示例。上述代码在数据库中会实际生成3个数据表。同时在对模型的编程过程中,子类
# 新建Mon对象,直接在子类中引用父类字段 m1 = Mon(user=‘ds‘,headline=‘sds‘) m1.content = ‘sddhjk‘ m1.save() # 通过父类引用父类字段 print(m1.messagebase.content)
4.4.3 代理继承
代理模型继承指父类用于在底层数据库中管理数据表,而子类不定义数据列。只定义查询数据集的排序方式等元数据。前两种继承模型中都有实际存储数据的作用,而代理模式继承中子类只用于管理父类的数据,而不实际存储数据。设置的方法是在Meta中定义proxy=True属性来实现,示例代码如下:
class MessageBase(models.Model): id = models.AutoField() content = models.CharField(max_length=100) user = models.CharField(max_length=20) pub_date = models.DateTimeField() class Mon(MessageBase): class Meta: proxy = True ordering = ["-pub_date"]
Django视图层的主要作用是衔接HTML模板、python程序、HTTP请求等。
URL分发映射配置可以被看做Django项目的入口配置,通过URL分发可以指定用户的每一个访问的后台python处理函数是什么。
每一个django项目都有一个urls.py文件用于维护自身的URL dispatcher,该文件的基本内容如下:
from django.contrib import admin from django.urls import path from django.conf.urls import url from . import views urlpatterns = [ url(r‘^welcome‘,views.welcome), url(r‘^moments_input/‘,views.moments_input), ]
程序通过维护urlpatterns列表中的元素完成URL的映射,每个元素都是一个django.conf.urls.url的实例,函数url()的第1个参数是HTTP路径,第2个参数是该路径映射到的python函数名。
正则表达式速查表
符号 |
说明 |
例子 |
\ |
将下一个字符标记为特殊字符 |
“\n”匹配一个换行符 “\\”匹配一个“\” “\(”匹配一个“(” |
^ |
字符串的开始位置 |
“^abc”:以abc开头 |
$ |
字符串的结束位置 |
“abc$”:以abc结尾 |
* |
前面的子表达式0或者多次 |
“2*”匹配“”“2”“2222”等 |
+ |
前面的子表达式1或者多次 |
“2+”匹配“1”“2”“222”等 |
? |
前面的子表达式0或者1次 |
“3?”匹配“”或者“3” |
. |
除\n以外的任意单个字符 |
|
{n} |
匹配n次 |
“o{2}”:匹配food中的两个o |
{n,} |
至少匹配n次 |
|
{n,m} |
匹配n到m次 |
|
x|y |
匹配x或者y |
Asd|sdf:匹配Asd或者sdf |
[xyz] |
匹配xyz中任一字符 |
[123]匹配1,2或者3 |
[a-z] |
匹配a-z范围内的字符 |
|
[^a-z] |
匹配不在a-z范围内的字符 |
|
5.1.3 命名URL映射
在普通的URL映射中,Django将URL中的变量参数按照路径中的出现顺序传递给被调用函数。而命名URL参数映射使得开发者可以定义这些被传递参数的名称,命名URL参数的定义方式为 ?p<param_name>pattern,示例代码如下:
from django.conf.urls import url from . import views urlpatterns = [ url(r‘^year/2015/$‘,views.year2015), url(r‘^year/?p<year>([0-9]{4})/$‘,views.year), url(r‘^year/?p<year>([0-9]{4})/?p<month>([0-9]{2})/$‘,views.month), ]
上述代码中的后两个url()使用命名参数进行了定义,他们调用Views.py文件中的相关函数,调用的方式为year(request,year=xxxx)和month(request,year=xxxx,month=xx)。
当多个URL映射到一个地址是,只选择第一个映射。
在实际的大型项目开发中,一个项目可能包含多个Django应用,而每一个都有自己的URL映射规则。这个时候讲所有的URL映射都保存在一个文件中非常不利于对项目进行维护,所以Django提供了include()函数来进行分布式的URL映射。在项目djangotest/djangotest/urls/py的文件中引用其他应用的URL映射文件的代码如下:
from django.contrib import admin from django.urls import path from django.conf.urls import include,url urlpatterns = [ url(‘admin/‘, admin.site.urls), url(r‘^app/‘, include(‘app.urls‘)), ]
5.1.5 反向URL映射
除了以上介绍的几种映射方式,Django还提供了反向的从映射名到URL地址的解析功能。URL反向解析使得开发者可以用映射名代替很多需要写绝对URL路径的地方,提高了代码的可维护性。
待续。。。非常重要
视图函数是Django开发者处理HTTP请求的python函数。在通常的情况下,视图函数的功能是通过模型层对象处理数据,然后用如下所示的一种方式返回数据。
对于以下简单的页面,可以直接在视图函数中构造返回给客户端的字符串,该功能是通过HttpResponse()函数封装返回。
from django.http import HttpResponse def index(request): HttpResponse(‘<h2>我是你爸爸!</h2>‘)
from django.shortcuts import render def index(request): render(request,‘index.html‘)
render()函数的第一个参数是HTTP Request,第二个参数是模板文件名,第三个参数是想模板文件中返回的数据,是字典的形式。
5.2.3 返回HTTP错误
HTTP的错误通过HTTP头中的status来进行表述,通过给httpResponse函数传递status参数可以返回HTTP的错误或者状态。如:
from django.http import HttpResponseRedirect,HttpResponse def index(request): HttpResponse(status=404)
通过上述的代码可以返回HTTP 404错误。但是为了方便开发者的使用,Django对常用的status状态进行了进一步的封装,具体的实现如下所示:
模板文件是一种文本文件,主要又目标文件的内容组成,然后辅以模板的特殊语法用于替换动态内容。下面的代码是一个功能较为齐全的模板文件。
{% extend "base.html" %} # 继承基类模板 {% block title %}title 内容{% endblock %} {% block content %} <h1>{{ section.title }}</h1> {% for i in ins %} <h3> {{ i.code | upper }} </h3> {% endfor %} {% endblock %}
{% for moment in moments %} <h3> {{ moment.user | upper }} </h3> {% endfor %} {% if moment.id < 10 %} <h3>{{ moment.headline }}</h3> {% elif moment.id < 20 %} <h3>{{ moment.headline }}</h3> {% else %} <h3>{{ moment.headline }}</h3> {% endif %}
过滤器在模板中是放在变量后面用于对变量显示的结果进行控制的技术,变量和过滤器之间用管道符号进行连接。具体的过滤器如下图所示。
过滤器 |
说明 |
|
|
|
|
原文:https://www.cnblogs.com/wl443587/p/10968744.html