定义属性
定义属性时,需要字段类型
字段类型被定义在django.db.models.fields目录下,为了方便使用,被导入到django.db.models中
使用方式
导入from django.db import models
通过models.Field创建字段类型的对象,赋值给属性
对于重要数据都做逻辑删除,不做物理删除,实现方法是定义isDelete属性,类型为BooleanField,默认值为False
字段类型
AutoField:一个根据实际ID自动增长的IntegerField,通常不指定
如果不指定,一个主键字段将自动添加到模型中
BooleanField:true/false 字段,此字段的默认表单控制是CheckboxInput
NullBooleanField:支持null、true、false三种值
CharField(max_length=字符长度):字符串,默认的表单样式是 TextInput
TextField:大文本字段,一般超过4000使用,默认的表单控件是Textarea
IntegerField:整数
DecimalField(max_digits=None, decimal_places=None):使用python的Decimal实例表示的十进制浮点数
DecimalField.max_digits:位数总数
DecimalField.decimal_places:小数点后的数字位数
FloatField:用Python的float实例来表示的浮点数
TimeField:使用Python的datetime.time实例表示的时间,参数同DateField
DateTimeField:使用Python的datetime.datetime实例表示的日期和时间,参数同DateField
FileField:一个上传文件的字段
ImageField:继承了FileField的所有属性和方法,但对上传的对象进行校验,确保它是个有效的image
DateField[auto_now=False, auto_now_add=False]):使用Python的datetime.date实例表示的日期
参数DateField.auto_now:每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,
它总是使用当前日期,默认为false
参数DateField.auto_now_add:当对象第一次被创建时自动设置当前时间,用于创建的时间戳,
它总是使用当前日期,默认为false
该字段默认对应的表单控件是一个TextInput. 在管理员站点添加了一个JavaScript写的日历控件,和一个“Today"的快捷按钮,
包含了一个额外的invalid_date错误消息键
auto_now_add, auto_now, and default 这些设置是相互排斥的,他们之间的任何组合将会发生错误的结果
字段选项
通过字段选项,可以实现对字段的约束
在字段对象时通过关键字参数指定
db_column:字段的名称,如果未指定,则使用属性的名称
db_index:若值为 True, 则在表中会为此字段创建索引
default:默认值
primary_key:若为 True, 则该字段会成为模型的主键字段
unique:如果为 True, 这个字段在表中必须有唯一值
max_length:字段最大的长度
null:如果为True,Django 将空值以NULL 存储到数据库中,默认值是 False
blank:如果为True,则该字段允许为空白,默认值是 False
对比:null是数据库范畴的概念,blank是表单验证证范畴的
关系
关系的类型包括
ForeignKey:一对多,将字段定义在多的端中
ManyToManyField:多对多,将字段定义在两端中
OneToOneField:一对一,将字段定义在任意一端中
可以维护递归的关联关系,使用'self'指定,详见“自关联”
#示例
from django.db import models # Create your models here. class BookInfo(models.Model): btitle = models.CharField(max_length=20) bpub_date = models.DateTimeField() def __str__(self): return "%d" % self.pk class HeroInfo(models.Model): hname = models.CharField(max_length=20) hgender = models.BooleanField() hcontent = models.CharField(max_length=100) hBook = models.ForeignKey('BookInfo') def __str__(self): return "%d" % self.pk
- 用一访问多:对象.模型类小写_set
bookinfo.heroinfo_set
- 用一访问一:对象.模型类小写
heroinfo.bookinfo
- 访问id:对象.属性_id
heroinfo.book_id
元选项
- 在模型类中定义类Meta,用于设置元信息
- 元信息db_table:定义数据表名称,推荐使用小写字母,数据表的默认名称
<app_name>_<model_name>
- ordering:对象的默认排序字段,获取对象的列表时使用,接收属性构成的列表
class BookInfo(models.Model):
...
class Meta():
ordering = ['id']
- 字符串前加-表示倒序,不加-表示正序
class BookInfo(models.Model):
...
class Meta():
ordering = ['-id']
- 排序会增加数据库的开销
示例演示
创建test2项目,并创建booktest应用,使用mysql数据库
- 定义图书模型
class BookInfo(models.Model): btitle = models.CharField(max_length=20) bpub_date = models.DateTimeField() bread = models.IntegerField(default=0) bcommet = models.IntegerField(default=0) isDelete = models.BooleanField(default=False)
- 英雄模型
class HeroInfo(models.Model): hname = models.CharField(max_length=20) hgender = models.BooleanField(default=True) isDelete = models.BooleanField(default=False) hcontent = models.CharField(max_length=100) hbook = models.ForeignKey('BookInfo')
- 定义index、detail视图
# Create your views here. from django.http import HttpResponse from django.http import HttpResponseRedirect from django.template import loader from django.urls import reverse from booktest.models import BookInfo def index(request): # return HttpResponse("欢迎来到 Django首页!") booklist = BookInfo.objects.all() template = loader.get_template('booktest/index.html') # context = RequestContext(request, {'booklist': booklist}) context = {'booklist': booklist} return HttpResponse(template.render(context)) def detail(request, id): # return HttpResponse("DETAIL %s" % id) book = BookInfo.objects.get(pk=id) template = loader.get_template('booktest/detail.html') # context = RequestContext(request, {'book': book}) context = {'book': book} return HttpResponse(template.render(context)) def add(request): a = request.GET['a'] b = request.GET['b'] c = int(a) + int(b) return HttpResponse(str(c)) def add2(request, a, b): c = int(a) + int(b) return HttpResponse(str(c)) from django.http import HttpResponseRedirect from django.core.urlresolvers import reverse # django 1.4.x - django 1.10.x # from django.urls import reverse # new in django 1.10.x def old_detail(request, a, ): return HttpResponseRedirect( reverse('detail', args=(a)) )
- index.html、detail.html模板
<!DOCTYPE html> <html> <head> <title>首页</title> </head> <body> <h1>图书列表</h1> <ul> {% for book in booklist %} <li> {# {% comment %}<a href="{{ book.id }}">{% endcomment %}#} <a href="{% url 'detail' book.id %}"> {{ book.btitle }} </a> </li> {% endfor %} </ul> </body> </html> <!DOCTYPE html> <html> <head> <title>详细页</title> </head> <body> <h1>{{ book.btitle }}</h1> <ul> {% for hero in book.heroinfo_set.all %} <li>{{ hero.hname }}---{{ hero.hcontent }}</li> {% endfor %} </ul> </body> </html>
- 配置url,能够完成图书及英雄的展示
from django.conf.urls import url, include from django.contrib import admin from booktest import views urlpatterns = [ url(r'^add/$', views.add, name='add'), url(r'^add/(\d+)/(\d+)/$', views.add2, name='add2'), url(r'^$', views.index), url(r'^([0-9]+)/$', views.old_detail), url(r'^book/([0-9]+)/$', views.detail, name="detail"), url(r'^admin/', admin.site.urls), ]
测试数据
- 模型BookInfo的测试数据
insert into booktest_bookinfo(btitle,bpub_date,bread,bcommet,isDelete) values ('射雕英雄传','1980-5-1',12,34,0), ('天龙八部','1986-7-24',36,40,0), ('笑傲江湖','1995-12-24',20,80,0), ('雪山飞狐','1987-11-11',58,24,0)
- 模型HeroInfo的测试数据
insert into booktest_heroinfo(hname,hgender,hbook_id,hcontent,isDelete) values ('郭靖',1,1,'降龙十八掌',0), ('黄蓉',0,1,'打狗棍法',0), ('黄药师',1,1,'弹指神通',0), ('欧阳锋',1,1,'蛤蟆功',0), ('梅超风',0,1,'九阴白骨爪',0), ('乔峰',1,2,'降龙十八掌',0), ('段誉',1,2,'六脉神剑',0), ('虚竹',1,2,'天山六阳掌',0), ('王语嫣',0,2,'神仙姐姐',0), ('令狐冲',1,3,'独孤九剑',0), ('任盈盈',0,3,'弹琴',0), ('岳不群',1,3,'华山剑法',0), ('东方不败',0,3,'葵花宝典',0), ('胡斐',1,4,'胡家刀法',0), ('苗若兰',0,4,'黄衣',0), ('程灵素',0,4,'医术',0), ('袁紫衣',0,4,'六合拳',0)
自定义Field
Django 的官方提供了很多的 Field,但是有时候还是不能满足我们的需求,不过Django提供了自定义 Field 的方法:
来一个简单的例子吧。
1. 减少文本的长度,保存数据的时候压缩,读取的时候解压缩,如果发现压缩后更长,就用原文本直接存储:
Django 1.7 以下
from django.db import models class CompressedTextField(models.TextField): """ model Fields for storing text in a compressed format (bz2 by default) """ __metaclass__ = models.SubfieldBase def to_python(self, value): if not value: return value try: return value.decode('base64').decode('bz2').decode('utf-8') except Exception: return value def get_prep_value(self, value): if not value: return value try: value.decode('base64') return value except Exception: try: tmp = value.encode('utf-8').encode('bz2').encode('base64') except Exception: return value else: if len(tmp) > len(value): return value return tmp
- to_python 函数用于转化数据库中的字符到 Python的变量,
- get_prep_value 用于将Python变量处理后(此处为压缩)保存到数据库,
- 使用和Django自带的 Field 一样。
Django 1.8 以上版本,可以用
#coding:utf-8 from django.db import models class CompressedTextField(models.TextField): """ model Fields for storing text in a compressed format (bz2 by default) """ def from_db_value(self, value, expression, connection, context): if not value: return value try: return value.decode('base64').decode('bz2').decode('utf-8') except Exception: return value def to_python(self, value): if not value: return value try: return value.decode('base64').decode('bz2').decode('utf-8') except Exception: return value def get_prep_value(self, value): if not value: return value try: value.decode('base64') return value except Exception: try: return value.encode('utf-8').encode('bz2').encode('base64') except Exception: return value
Django 1.8及以上版本中,from_db_value 函数用于转化数据库中的字符到 Python的变量。
2. 比如我们想保存一个 列表到数据库中,在读取用的时候要是 Python的列表的形式,我们来自己写一个 ListField:
这个ListField继承自 TextField,代码如下:
from django.db import models import ast class ListField(models.TextField): __metaclass__ = models.SubfieldBase description = "Stores a python list" def __init__(self, *args, **kwargs): super(ListField, self).__init__(*args, **kwargs) def to_python(self, value): if not value: value = [] if isinstance(value, list): return value return ast.literal_eval(value) def get_prep_value(self, value): if value is None: return value return unicode(value) # use str(value) in Python 3 def value_to_string(self, obj): value = self._get_val_from_obj(obj) return self.get_db_prep_value(value)
使用它很简单,首先导入 ListField,像自带的 Field 一样使用:
class Article(models.Model): labels = ListField()
在终端上尝试(运行 python manage.py shell 进入):
>>> from app.models import Article
>>> d = Article()
>>> d.labels
[]
>>> d.labels = ["Python", "Django"]
>>> d.labels
["Python", "Django"]
>>> from blog.models import Article
>>> a = Article()
>>> a.labels.append('Django')
>>> a.labels.append('custom fields')
>>> a.labels
['Django', 'custom fields']
>>> type(a.labels)
<type 'list'>
>>> a.content = u'我正在写一篇关于自定义Django Fields的教程'
>>> a.save()
参考来源:http://code.ziqiangxuetang.com/django/django-custom-field.html