Django之Models操作
目录:
module是django提供的ORM模型,其他的python的web框架就只能使用像SQLAlchemy来实现,django的提供的module比SQLAlchemy强大,并且django框架的其他方法也可以获取module或根据module来实现一些功能。
别人问我,django是什么,我都告诉他django是python最屌的框架,没有之一。
module连接数据库
对于module,操作ORM首先要有数据库,默认使用SQLite3,还支持MySQL,Oracle和PostgreSQL
sqlite的引擎为django.db.backends.sqlite3,mysql的引擎为django.db.backends.mysql
配置在setting中
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'dbname',
'USER': 'root',
'PASSWORD': 'password',
'HOST': 'hostname',
'PORT': '3306',
}
}
需要安装MySQLdb模块,如果想使用例如pymysql也是可以的,需要在__init__.py
文件中配置一下,可以参考
创建表
表结构
创建表的时候需要继承models.Model
from django.db import models
class userinfo(models.Model):
name = models.CharField(max_length=30)
password = models.CharField(max_length=30)
introduce = models.TextField()
表字段
- AutoField(Field) int类型,如果要设置为自增列需要添加参数primary_key=True
- BigAutoField(AutoField) bigint类型,如果要设置为自增列需要添加参数primary_key=True,对于django,如果表没有自增列,会进行自动创建名为id的自增整数列
- SmallIntegerField(IntegerField) 小整数,范围为 -32768~32767
- PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)正小整数,范围为0~32767
- IntegerField(Field) 整数列,有符号,范围为-2147483648~2147483647
- PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField) 正整数,范围为0~2147483647
- BigIntegerField(IntegerField) 长整型,有符号,范围为-9223372036854775808~9223372036854775807
- BooleanField(Field) 布尔值类型
- NullBooleanField(Field) 可以为空的布尔值类型
- CharField(Field)字符串类型,必须提供表示字符长度的max_length参数
- TextField(Field)文本类型
- EmailField(CharField)字符串类型,django admin和module form中提供验证机制
- IPAddressField(Field)字符串类型,django admin和module form中提供验证IPV4的机制
- GenericIPAddressField(Field)字符串类型,django admin和module form中提供验证IPV4和IPV6的机制,通过protocol参数可指定Ipv4或Ipv6,参数值为'both',"ipv4","ipv6",unpack_ipv4参数如果指定为True,则输入::ffff:192.0.2.1时候,可解析为192.0.2.1,开启此功能,需要protocol="both"
- URLField(CharField)字符串类型,django admin和module form中提供验证url的机制
- SlugField(CharField)字符串类型,django admin和module form中提供验证字母、数字、下划线、连接符(减号)的机制
- CommaSeparatedIntegerField(CharField)字符串类型,django admin和module form中提供验证格式为逗号分隔的数字
- FilePathField(Field)字符串类型,django admin和module form中提供读取文件夹下文件的功能,参数有path文件夹目录,match=None正则匹配,recursive=False递归下面的文件夹,
allow_files=True
允许文件,allow_folders=False
允许文件夹 - FileField(Field)字符串,路径保存在数据库,文件上传到指定目录,参数有upload_to=""上传文件的保存路径和storage=None存储组件,默认为django.core.files.storage.FileSystemStorage
- ImageField(FileField)字符串类型,路径保存在数据库,文件上传到指定目录,参数有
upload_to=""
上传文件的保存路径,storage=None
存储组件,默认django.core.files.storage.FileSystemStorage,width_field=None
上传图片的高度保存的数据库字段名(字符串),height_field=None
上传图片的宽度保存的数据库字段名(字符串) - UUIDField(Field)字符串类型,django admin和module form中提供验证UUID格式的机制
- DateTimeField(DateField)日期+时间格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ]
- DateField(DateTimeCheckMixin, Field) 日期格式YYYY-MM-DD
- TimeField(DateTimeCheckMixin, Field) 时间格式HH:MM[:ss[.uuuuuu]]
- DurationField(Field)长整数,时间间隔,数据库中按照bigint存储,ORM中获取的值为datetime.timedelta类型
- FloatField(Field)浮点型
- DecimalField(Field)10进制小数,参数有
max_digits
小数总长度和decimal_places
小数位长度 - BinaryField(Field)二进制类型
字段参数
数据库字段参数
- null 数据库中字段是否可以为空
db_column
数据库中字段的列名重新命名name = models.CharField(max_length=30)
,在数据库中列名为name,如果想使用别的名称就用这个参数- db_tablespace
- default 数据库中字段的默认值
- primary_key 数据库中字段是否为主键
- db_index 数据库中字段是否可以建立索引
- unique 数据库中字段是否可以建立唯一索引
unique_for_date
数据库中字段【日期】部分是否可以建立唯一索引unique_for_month
数据库中字段【月】部分是否可以建立唯一索引unique_for_year
数据库中字段【年】部分是否可以建立唯一索引auto_now
数据库中该字段的操作时间,创建和修改都算操作auto_now_add
数据库中该字段的创建时间
admin参数
- verbose_name Admin中显示的字段名称
- blank Admin中是否允许用户输入为空,用于admin的表单验证功能
- editable Admin中是否可以编辑
- help_text Admin中该字段的提示信息
- choices Admin中显示选择框的内容,用不变动的数据放在内存中从而避免跨表操作,如:favor= models.IntegerField(choices=[(0, 'python'),(1, 'linux'),],default=1),由django在内存中创建,与数据库无关
form表单参数
- error_messages 自定义错误信息
- validators 自定义错误验证
error_messages为(字典类型),从而定制想要显示的错误信息,字典健:null, blank, invalid, invalid_choice, unique, and unique_for_date,例如:{'null': "不能为空.", 'invalid': '格式错误'}
validators为(列表类型),从而定制想要的验证规则
示例代码,会在后边form表单验证的时候进行具体实现方式
from django.core.validators import RegexValidator
from django.core.validators import EmailValidator,URLValidator,DecimalValidator,\
MaxLengthValidator,MinLengthValidator,MaxValueValidator,MinValueValidator
如:
test = models.CharField(
max_length=32,
error_messages={
'c1': '优先错信息1',
'c2': '优先错信息2',
'c3': '优先错信息3',
},
validators=[
RegexValidator(regex='root_\d+', message='错误了', code='c1'),
RegexValidator(regex='root_112233\d+', message='又错误了', code='c2'),
EmailValidator(message='又错误了', code='c3'), ]
)
表元数据信息
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
username = models.CharField(max_length=32)
class Meta:
# 数据库中生成的表名称 默认 app名称 + 下划线 + 类名
db_table = "table_name"
# 联合索引
index_together = [
("pub_date", "deadline"),
]
# 联合唯一索引
unique_together = (("driver", "restaurant"),)
# admin中显示的表名称
verbose_name
# verbose_name加s
verbose_name_plural
更多的可以参考官方文档
创建单表
modules.py代码
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class userinfo(models.Model):
name = models.CharField(max_length=30)
password = models.CharField(max_length=30)
introduce = models.TextField()
setting.py代码中的INSTALLED_APPS列表中添加对应的app
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app'
]
同步表结构到数据库
C:\pycode\mysite>python manage.py makemigrations
Migrations for 'app':
app\migrations\0001_initial.py
- Create model userinfo
C:\pycode\mysite>python manage.py migrate
Operations to perform:
Apply all migrations: admin, app, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying app.0001_initial... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying sessions.0001_initial... OK
在数据库中查询一下创建的表
mysql> use django_test
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+----------------------------+
| Tables_in_django_test |
+----------------------------+
| app_userinfo |
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
+----------------------------+
11 rows in set (0.00 sec)
mysql> show create table app_userinfo \G;
*************************** 1. row ***************************
Table: app_userinfo
Create Table: CREATE TABLE `app_userinfo` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(30) NOT NULL,
`password` varchar(30) NOT NULL,
`introduce` longtext NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
ERROR:
No query specified
可以看到会为userinfo表创建一个id的非空自增字段
表操作
基本操作
增删改查
用于展示的代码
原理是通过请求来触发增删改查操作
url配置
from django.conf.urls import url, include
from . import views
urlpatterns = [
url(r'articles/(?P<year>[0-9]{4})/$', views.year_archive,{ 'mouth': '09' }),
url(r'add/$', views.add),
]
views逻辑
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.shortcuts import render, HttpResponse
from app.models import userinfo
def add(request):
if request.method == 'GET':
return render(request, "index.html")
if request.method == 'POST':
name = request.POST.get('name')
password = request.POST.get('password')
obj = userinfo.objects.create(name=name,password=password)
obj.save()
return HttpResponse('yes')
else:
return HttpResponse('no')
models表结构,参考创建单表
index的html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<link rel="stylesheet" href="/static/why.css">
</head>
<body>
<form action="/app/add/" method="POST" >
<input type="text" name="name">
<input type="text" name="password">
<input type="submit" value="提交">
</form>
</body>
</html>
然后重启后访问
提交结果
数据库中也可以查询到结果
mysql> select * from app_userinfo;
+----+------+----------+-----------+
| id | name | password | introduce |
+----+------+----------+-----------+
| 1 | why | 123456 | |
+----+------+----------+-----------+
1 row in set (0.00 sec)
需要注意的是,这里<form action="/app/add/" method="POST" >
后边一定要有/
,如果没有加/
会报错误
主要的原因是django默认情况get请求会给url加上末尾的/
,但是post的请求到没有/
的url会报错的。
默认情况,APPEND_SLASH=True,设置项为是否开启URL访问地址后面不为/
跳转至带有/
的路径
增
刚才我们是from app.models import userinfo
的,就可以直接使用userinfo。
models.Tb1.objects.create(c1='xx', c2='oo') 增加一条数据,可以接受字典类型数据 **kwargs
obj = models.Tb1(c1='xx', c2='oo')
obj.save()
查
models.Tb1.objects.get(id=123) # 获取单条数据,不存在则报错(不建议)
models.Tb1.objects.all() # 获取全部
models.Tb1.objects.filter(name='seven') # 获取指定条件的数据
因为查询到的是一个对象,支持分片,例如
models.Tb1.objects.all()[10:20]
参考上一个例子,获取到的结果
obj = userinfo.objects.all()
print obj
print type(obj)
print obj[0]
print type(obj[0])
print obj[0].name
print type(obj[0].name)
获取结果
<QuerySet [<userinfo: userinfo object>]>
<class 'django.db.models.query.QuerySet'>
userinfo object
<class 'app.models.userinfo'>
why
<type 'unicode'>
即all获取的是对象的列表。
上大学的时候代码虽然写的不咋地,增删改查这个顺序倒是记得很熟,因为删和改都是要在查的基础上完成的,就把查提前了
删
models.Tb1.objects.filter(name='seven').delete() # 删除指定条件的数据
改
models.Tb1.objects.filter(name='seven').update(gender='0') # 将指定条件的数据更新,均支持 **kwargs
或者
obj = models.Tb1.objects.get(id=1)
obj.c1 = '111'
obj.save() # 修改单条数据
获取查询的SQL
obj = models.Tb1.objects.all()
obj.query
查的进阶操作
获取个数
models.Tb1.objects.filter(name='seven').count()
大于,小于
models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值
models.Tb1.objects.filter(id__gte=1) # 获取id大于等于1的值
models.Tb1.objects.filter(id__lt=10) # 获取id小于10的值
models.Tb1.objects.filter(id__lte=10) # 获取id小于10的值
models.Tb1.objects.filter(id__lt=10, id__gt=1) # 获取id大于1 且 小于10的值
in
models.Tb1.objects.filter(id__in=[11, 22, 33]) # 获取id等于11、22、33的数据
models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in
isnull
Entry.objects.filter(pub_date__isnull=True)
contains
models.Tb1.objects.filter(name__contains="ven")
models.Tb1.objects.filter(name__icontains="ven") # icontains大小写不敏感
models.Tb1.objects.exclude(name__icontains="ven")
其他类似的有startswith,istartswith, endswith, iendswith
一些方法和SQL语句对应关系也可以参考
operators = {
'exact': '= %s',
'iexact': 'LIKE %s',
'contains': 'LIKE BINARY %s',
'icontains': 'LIKE %s',
'regex': 'REGEXP BINARY %s',
'iregex': 'REGEXP %s',
'gt': '> %s',
'gte': '>= %s',
'lt': '< %s',
'lte': '<= %s',
'startswith': 'LIKE BINARY %s',
'endswith': 'LIKE BINARY %s',
'istartswith': 'LIKE %s',
'iendswith': 'LIKE %s',
}
范围(range)
models.Tb1.objects.filter(id__range=[1, 2]) # 范围betwwen and
排序(order by)
models.Tb1.objects.filter(name='seven').order_by('id') # 正序(asc)
models.Tb1.objects.filter(name='seven').order_by('-id') # 倒叙(desc)
分组(group by)
from django.db.models import Count, Min, Max, Sum
models.Tb1.objects.filter(c1=1).values('id').annotate(c=Count('num'))
对应的SQL语句为
SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
这里values('id').annotate(c=Count('num'))是连在一起的,意思是根据id进行分组,拿到num列的个数
limit 、offset
regex正则匹配,iregex 不区分大小写
Entry.objects.get(title__regex=r'^(An?|The)+')
Entry.objects.get(title__iregex=r'^(an?|the)+')
date
Entry.objects.filter(pub_date__date=datetime.date(2005, 1, 1))
Entry.objects.filter(pub_date__date__gt=datetime.date(2005, 1, 1))
year
Entry.objects.filter(pub_date__year=2005)
Entry.
objects.filter(pub_date__year__gte=2005)
month
Entry.objects.filter(pub_date__month=12)
Entry.objects.filter(pub_date__month__gte=6)
day
Entry.objects.filter(pub_date__day=3)
Entry.objects.filter(pub_date__day__gte=3)
week_day
Entry.objects.filter(pub_date__week_day=2)
Entry.objects.filter(pub_date__week_day__gte=2)
hour
Event.objects.filter(timestamp__hour=23)
Event.objects.filter(time__hour=5)
Event.objects.filter(timestamp__hour__gte=12)
minute
Event.objects.filter(timestamp__minute=29)
Event.objects.filter(time__minute=46)
Event.objects.filter(timestamp__minute__gte=29)
second
Event.objects.filter(timestamp__second=31)
Event.objects.filter(time__second=2)
Event.objects.filter(timestamp__second__gte=31)
其他操作
extra
extra(self, select=None, where=None, params=None, tables=None, order_by=None, select_params=None)
Entry.objects.extra(select={'new_id': "select col from sometable where othercol > %s"}, select_params=(1,))
Entry.objects.extra(where=['headline=%s'], params=['Lennon'])
Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])
Entry.objects.extra(select={'new_id': "select id from tb where id > %s"}, select_params=(1,), order_by=['-nid'])
F
F操作更多的是使在update的时候在字段数据的基础上进行操作,例如涨价,打折等
from django.db.models import F
models.Tb1.objects.update(num=F('num')+1)
Q
Q操作是为了实现和与或关系的问题
方式一:
Q(nid__gt=10)
Q(nid=8) | Q(nid__gt=10)
Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
方式二:
con = Q()
q1 = Q()
q1.connector = 'OR'
q1.children.append(('id', 1))
q1.children.append(('id', 10))
q1.children.append(('id', 9))
q2 = Q()
q2.connector = 'OR'
q2.children.append(('c1', 1))
q2.children.append(('c1', 10))
q2.children.append(('c1', 9))
con.add(q1, 'AND')
con.add(q2, 'AND')
models.Tb1.objects.filter(con)
执行原生SQL
from django.db import connection, connections
cursor = connection.cursor() # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
row = cursor.fetchone()
连表结构
连表结构一般有三种
- 一对多:models.ForeignKey(其他表)
- 多对多:models.ManyToManyField(其他表)
- 一对一:models.OneToOneField(其他表)
这三种连表结构的应用场景
- 一对多:当一张表中创建一行数据时,有一个单选的下拉框(可以被重复选择) 例如:创建用户信息时候,需要选择一个用户类型【普通用户】【金牌用户】【铂金用户】等。
- 多对多:在某表中创建一行数据是,有一个可以多选的下拉框 例如:创建用户信息,需要为用户指定多个爱好
- 一对一:在某表中创建一行数据时,有一个单选的下拉框(下拉框中的内容被用过一次就消失了) 例如:原有含10列数据的一张表保存相关信息,经过一段时间之后,10列无法满足需求,需要为原来的表再添加5列数据
django建立联表
model代码
class UserProfile(models.Model):
user_info = models.OneToOneField('UserInfo')
username = models.CharField(max_length=64)
password = models.CharField(max_length=64)
def __unicode__(self):
return self.username
class UserInfo(models.Model):
user_type_choice = (
(0, u'普通用户'),
(1, u'高级用户'),
)
user_type = models.IntegerField(choices=user_type_choice)
name = models.CharField(max_length=32)
email = models.CharField(max_length=32)
address = models.CharField(max_length=128)
def __unicode__(self):
return self.name
class UserGroup(models.Model):
caption = models.CharField(max_length=64)
user_info = models.ManyToManyField('UserInfo')
def __unicode__(self):
return self.caption
class Host(models.Model):
hostname = models.CharField(max_length=64)
ip = models.GenericIPAddressField()
user_group = models.ForeignKey('UserGroup')
def __unicode__(self):
return self.hostname
上述表结构用ER图来表示为
更多用于外键参数
ForeignKey(ForeignObject) # ForeignObject(RelatedField)
to, # 要进行关联的表名
to_field=None, # 要关联的表中的字段名称
on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为
- models.CASCADE,删除关联数据,与之关联也删除
- models.DO_NOTHING,删除关联数据,引发错误IntegrityError
- models.PROTECT,删除关联数据,引发错误ProtectedError
- models.SET_NULL,删除关联数据,与之关联的值设置为null(前提FK字段需要设置为可空)
- models.SET_DEFAULT,删除关联数据,与之关联的值设置为默认值(前提FK字段需要设置默认值)
- models.SET,删除关联数据,
a. 与之关联的值设置为指定值,设置:models.SET(值)
b. 与之关联的值设置为可执行对象的返回值,设置:models.SET(可执行对象)
def func():
return 10
class MyModel(models.Model):
user = models.ForeignKey(
to="User",
to_field="id"
on_delete=models.SET(func),)
related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件:
# 如:
- limit_choices_to={'nid__gt': 5}
- limit_choices_to=lambda : {'nid__gt': 5}
from django.db.models import Q
- limit_choices_to=Q(nid__gt=10)
- limit_choices_to=Q(nid=8) | Q(nid__gt=10)
- limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
db_constraint=True # 是否在数据库中创建外键约束
parent_link=False # 在Admin中是否显示关联数据
OneToOneField(ForeignKey)
to, # 要进行关联的表名
to_field=None # 要关联的表中的字段名称
on_delete=None, # 当删除关联表中的数据时,当前表与其关联的行的行为
###### 对于一对一 ######
# 1. 一对一其实就是 一对多 + 唯一索引
# 2.当两个类之间有继承关系时,默认会创建一个一对一字段
# 如下会在A表中额外增加一个c_ptr_id列且唯一:
class C(models.Model):
nid = models.AutoField(primary_key=True)
part = models.CharField(max_length=12)
class A(C):
id = models.AutoField(primary_key=True)
code = models.CharField(max_length=1)
ManyToManyField(RelatedField)
to, # 要进行关联的表名
related_name=None, # 反向操作时,使用的字段名,用于代替 【表名_set】 如: obj.表名_set.all()
related_query_name=None, # 反向操作时,使用的连接前缀,用于替换【表名】 如: models.UserGroup.objects.filter(表名__字段名=1).values('表名__字段名')
limit_choices_to=None, # 在Admin或ModelForm中显示关联数据时,提供的条件:
# 如:
- limit_choices_to={'nid__gt': 5}
- limit_choices_to=lambda : {'nid__gt': 5}
from django.db.models import Q
- limit_choices_to=Q(nid__gt=10)
- limit_choices_to=Q(nid=8) | Q(nid__gt=10)
- limit_choices_to=lambda : Q(Q(nid=8) | Q(nid__gt=10)) & Q(caption='root')
symmetrical=None, # 仅用于多对多自关联时,symmetrical用于指定内部是否创建反向操作的字段
# 做如下操作时,不同的symmetrical会有不同的可选字段
models.BB.objects.filter(...)
# 可选字段有:code, id, m1
class BB(models.Model):
code = models.CharField(max_length=12)
m1 = models.ManyToManyField('self',symmetrical=True)
# 可选字段有: bb, code, id, m1
class BB(models.Model):
code = models.CharField(max_length=12)
m1 = models.ManyToManyField('self',symmetrical=False)
through=None, # 自定义第三张表时,使用字段用于指定关系表
through_fields=None, # 自定义第三张表时,使用字段用于指定关系表中那些字段做多对多关系表
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=50)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(
Person,
through='Membership',
through_fields=('group', 'person'),
)
class Membership(models.Model):
group = models.ForeignKey(Group, on_delete=models.CASCADE)
person = models.ForeignKey(Person, on_delete=models.CASCADE)
inviter = models.ForeignKey(
Person,
on_delete=models.CASCADE,
related_name="membership_invites",
)
invite_reason = models.CharField(max_length=64)
db_constraint=True, # 是否在数据库中创建外键约束
db_table=None, # 默认创建第三张表时,数据库中表的名称
数据库建表情况
数据库中表
mysql> show tables;
+----------------------------+
| Tables_in_django_test |
+----------------------------+
| app_host |
| app_usergroup |
| app_usergroup_user_info |
| app_userinfo |
| app_userprofile |
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
+----------------------------+
15 rows in set (0.00 sec)
详细各表创建语句
app_userprofile
mysql> show create table app_userprofile \G;
*************************** 1. row ***************************
Table: app_userprofile
Create Table: CREATE TABLE `app_userprofile` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(64) NOT NULL,
`password` varchar(64) NOT NULL,
`user_info_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_info_id` (`user_info_id`),
CONSTRAINT `app_userprofile_user_info_id_18dfdc0a_fk_app_userinfo_id` FOREIGN KEY (`user_info_id`) REFERENCES `app_userinfo` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
ERROR:
No query specified
可以看到app_userprofile
除了类中的两个字段,django自动添加了主键索引的id字段和用于一对一的user_info_id
字段,UNIQUE KEY user_info_id
(user_info_id
)创建唯一索引,外键字段为app_userinfo
的id
app_userinfo
mysql> show create table app_userinfo \G;
ERROR 2006 (HY000): MySQL server has gone away
No connection. Trying to reconnect...
Connection id: 84
Current database: django_test
*************************** 1. row ***************************
Table: app_userinfo
Create Table: CREATE TABLE `app_userinfo` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(32) NOT NULL,
`address` varchar(128) NOT NULL,
`email` varchar(32) NOT NULL,
`user_type` int(11) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
ERROR:
No query specified
可以看到对于app_userinfo
,django仅仅添加了主键索引的id字段
app_usergroup_user_info
mysql> show create table app_usergroup_user_info \G;
*************************** 1. row ***************************
Table: app_usergroup_user_info
Create Table: CREATE TABLE `app_usergroup_user_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`usergroup_id` int(11) NOT NULL,
`userinfo_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `app_usergroup_user_info_usergroup_id_userinfo_id_d8c7ee6e_uniq` (`usergroup_id`,`userinfo_id`),
KEY `app_usergroup_user_info_userinfo_id_1cee34b5_fk_app_userinfo_id` (`userinfo_id`),
CONSTRAINT `app_usergroup_user_i_usergroup_id_f54ab88a_fk_app_userg` FOREIGN KEY (`usergroup_id`) REFERENCES `app_usergroup` (`id`),
CONSTRAINT `app_usergroup_user_info_userinfo_id_1cee34b5_fk_app_userinfo_id` FOREIGN KEY (`userinfo_id`) REFERENCES `app_userinfo` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
ERROR:
No query specified
app_usergroup_user_info
是django自动生成的用于映射多对多关系的表,有自增主键id,usergroup_id
和usergroup_id
字段,分别为app_usergroup
和app_usergroup
字段的外键,创建了usergroup_id
和userinfo_id
联合唯一索引
app_usergroup
mysql> show create table app_usergroup \G;
*************************** 1. row ***************************
Table: app_usergroup
Create Table: CREATE TABLE `app_usergroup` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`caption` varchar(64) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
ERROR:
No query specified
可以看到对于app_usergroup
,django仅仅添加了主键索引的id字段
app_host
mysql> show create table app_host \G;
*************************** 1. row ***************************
Table: app_host
Create Table: CREATE TABLE `app_host` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`hostname` varchar(64) NOT NULL,
`ip` char(39) NOT NULL,
`user_group_id` int(11) NOT NULL,
PRIMARY KEY (`id`),
KEY `app_host_user_group_id_9cc9827b_fk_app_usergroup_id` (`user_group_id`),
CONSTRAINT `app_host_user_group_id_9cc9827b_fk_app_usergroup_id` FOREIGN KEY (`user_group_id`) REFERENCES `app_usergroup` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)
ERROR:
No query specified
可以看到对于app_host
,和app_userprofile
表类似,只不过user_group_id
不是唯一的索引。
对于多对多的三种表创建方式
直接通过models的ManyToManyField方法
参考上述方式中的方法
通过SQLAlchemy的实现方式直接创建第三张表
参考SQLAlchemy博文中创建方式
通过models的ManyToManyField方法,然后定义through来实现对第三张表自定义字段
以上的方式ManyToManyField非常省事,而创建第三张表可以自行添加字段
这里主要是记录一下,如何通过django的ManyToManyField方法和through来创建第三张表的自定义字段。
through字段的值是第三张表的表名
class UserGroup(models.Model):
caption = models.CharField(max_length=64)
user_info = models.ManyToManyField('UserInfo', through='HostToUsergroup')
def __unicode__(self):
return self.caption
然后在HostToUsergroup定义多对多关系的字段和自定义字段,这样依然可以使用django的model的一些内置方法。
填充数据
普通填充方式
from app.models import *
省略方法
userinfo1 = UserInfo.objects.create(name='why',email='why@whysdomain.com',address='xierqi',user_type=1)
userinfo2 = UserInfo.objects.create(name='mb',email='mb@whysdomain.com',address='xierqi',user_type=0)
userprofile1 = UserProfile.objects.create(user_info_id=1,username='wanghongyu',password='123456')
userprofile2 = UserProfile.objects.create(user_info_id=2,username='mabiao',password='123456')
usergroup1 = UserGroup.objects.create(caption='ops')
usergroup2 = UserGroup.objects.create(caption='dev')
host1 = Host.objects.create(user_group_id=1,hostname='ops1.whysdomain.com',ip='10.30.100.1')
host2 = Host.objects.create(user_group_id=1,hostname='ops2.whysdomain.com',ip='10.30.100.2')
host3 = Host.objects.create(user_group_id=1,hostname='ops3.whysdomain.com',ip='10.30.100.3')
host4 = Host.objects.create(user_group_id=2,hostname='dev1.whysdomain.com',ip='10.30.101.1')
host5 = Host.objects.create(user_group_id=2,hostname='dev2.whysdomain.com',ip='10.30.101.2')
当然也可以批量添加
通过bulk_create批量添加数据
我们上述情况就可以认为是这个方式添加
for i in resultlist:
p = Account(name=i)
p.save()
使用django.db.models.query.QuerySet.bulk_create()批量创建对象,减少SQL查询次数
querysetlist=[]
for i in resultlist:
querysetlist.append(Account(name=i))
Account.objects.bulk_create(querysetlist)
对于上述就可以换成
host1 = Host(user_group_id=1,hostname='ops1.whysdomain.com',ip='10.30.100.1')
host2 = Host(user_group_id=1,hostname='ops2.whysdomain.com',ip='10.30.100.2')
host3 = Host(user_group_id=1,hostname='ops3.whysdomain.com',ip='10.30.100.3')
host4 = Host(user_group_id=2,hostname='dev1.whysdomain.com',ip='10.30.101.1')
host5 = Host(user_group_id=2,hostname='dev2.whysdomain.com',ip='10.30.101.2')
Host.objects.bulk_create([host1,host2,host3,host4,host5])
填充完成的数据
mysql> select * from app_userinfo;
+----+------+---------+--------------------+-----------+
| id | name | address | email | user_type |
+----+------+---------+--------------------+-----------+
| 1 | why | xierqi | why@whysdomain.com | 1 |
| 2 | mb | xierqi | mb@whysdomain.com | 0 |
+----+------+---------+--------------------+-----------+
2 rows in set (0.00 sec)
mysql> select * from app_userprofile;
+----+------------+----------+--------------+
| id | username | password | user_info_id |
+----+------------+----------+--------------+
| 1 | wanghongyu | 123456 | 1 |
| 2 | mabiao | 123456 | 2 |
+----+------------+----------+--------------+
2 rows in set (0.00 sec)
mysql> select * from app_usergroup;
+----+---------+
| id | caption |
+----+---------+
| 1 | ops |
| 2 | dev |
+----+---------+
2 rows in set (0.00 sec)
mysql> select * from app_host;
+----+---------------------+-------------+---------------+
| id | hostname | ip | user_group_id |
+----+---------------------+-------------+---------------+
| 1 | ops1.whysdomain.com | 10.30.100.1 | 1 |
| 2 | ops2.whysdomain.com | 10.30.100.2 | 1 |
| 3 | ops3.whysdomain.com | 10.30.100.3 | 1 |
| 4 | dev1.whysdomain.com | 10.30.101.1 | 2 |
| 5 | dev2.whysdomain.com | 10.30.101.2 | 2 |
+----+---------------------+-------------+---------------+
5 rows in set (0.00 sec)
一对一连表操作
first方法取值
user_info_obj = UserInfo.objects.filter(id=1).first()
print type(user_info_obj)
print user_info_obj.user_type
print user_info_obj.get_user_type_display()
print user_info_obj.userprofile.password
user_info_obj
获取的为UserInfo对象,可以通过该对象获取到对象的字段,例如user_info_obj.user_type
,对于有描述的可以通过get_user_type_display()
方法获取,而对于关联的userprofile表中的数据也可以进行查询,例如user_info_obj.userprofile.password
。
取值结果
<class 'app.models.UserInfo'>
1
高级用户
123456
这是通过first获取的,如果通过all方法获取到的就是对象的列表
all方法
user_info_obj = UserInfo.objects.all()
print type(user_info_obj)
print user_info_obj
for i in user_info_obj:
print type(i)
print i.user_type
print i.get_user_type_display()
print i.userprofile.password
获取结果
<class 'django.db.models.query.QuerySet'>
<QuerySet [<UserInfo: why>, <UserInfo: mb>]>
<class 'app.models.UserInfo'>
1
高级用户
123456
<class 'app.models.UserInfo'>
0
普通用户
123456
all获取的是一个可迭代的对象
连表查询
方式是通过values方法查询的时候通过"连表表明__字段"来取值
user_info_obj = UserInfo.objects.values('email', 'userprofile__username').all()
print type(user_info_obj)
for i in user_info_obj:
print type(i)
print i.keys()
print i.values()
return HttpResponse('yes')
获取结果
<class 'django.db.models.query.QuerySet'>
<type 'dict'>
[u'email', u'userprofile__username']
[u'why@whysdomain.com', u'wanghongyu']
<type 'dict'>
[u'email', u'userprofile__username']
[u'mb@whysdomain.com', u'mabiao']
可以看到在使用value方法的时候,可以通过表“表明__字段的方式进行连表取值”
关于正向查找和反向查找
以上都是反向查找,就是去查有外键表的数据,而正向查找,就是有外键的表去查找外键关联的表的字段,对于一对一,正向和反向查找都统一使用这种方式来实现。
user_profilter_obj = UserProfile.objects.filter(id=1).first()
print type(user_profilter_obj)
print user_profilter_obj.username
print user_profilter_obj.user_info.email
获取结果
<class 'app.models.UserProfile'>
wanghongyu
why@whysdomain.com
不同的是没有外键的表进行反向查找的时候,使用"表对象.有外键表名.有外键表字段"来获取外键字段
而有外键的表进行正向查找的时候,使用"表对象.类中外键字段.关联表字段",即user_profilter_obj.user_info.email
,这个user_info
是UserProfile类中的user_info = models.OneToOneField('UserInfo')
一对多操作
一对多的关系主要在usergroup和host之间,一个usergroup可以对应多个host,外键在host表中
正向查找
正向查找就是在host表中查usergroup表中的数据
host_obj = Host.objects.filter(id=1).first()
print host_obj.user_group.caption
获取结果
ops
可以看到进行正向查找和一对一的时候是一样的。
!!!!!!!注意,以下使用的user_group是Host_info中定义的那个user_group
host_obj = Host.objects.filter(user_group__caption='ops').all()
print host_obj
获取结果
<QuerySet [<Host: ops1.whysdomain.com>, <Host: ops2.whysdomain.com>, <Host: ops3.whysdomain.com>]>
如果通过values方法也能直接过去到caption字段的值
host_obj = Host.objects.filter(user_group__caption='ops').values('user_group__caption')
print host_obj
获取结果
<QuerySet [{u'user_group__caption': u'ops'}, {u'user_group__caption': u'ops'}, {u'user_group__caption': u'ops'}]>
对于使用all()和不使用all()获取到的都是列表的形式,而如果用first()取到的是一个字典
host_obj = Host.objects.filter(user_group__caption='ops').values('user_group__caption').first()
print host_obj
获取结果
{u'user_group__caption': u'ops'}
反向查找
反向查找就是在usergroup表中查host表中的数据
user_group_obj = UserGroup.objects.filter(id=1).first()
print user_group_obj.host_set.all()
for i in user_group_obj.host_set.all():
print i.hostname
获取结果
<QuerySet [<Host: ops1.whysdomain.com>, <Host: ops2.whysdomain.com>, <Host: ops3.whysdomain.com>]>
ops1.whysdomain.com
ops2.whysdomain.com
ops3.whysdomain.com
对于使用all()和不使用all()获取到的都是列表的形式,而如果用first()取到的是一个字典
user_group_obj = UserGroup.objects.filter(host__hostname='dev1.whysdomain.com').first()
print user_group_obj.caption
获取结果
dev
通过values方法
user_group_obj = UserGroup.objects.filter(host__hostname='dev1.whysdomain.com').values('host__hostname')
print user_group_obj
获取结果
<QuerySet [{u'host__hostname': u'dev1.whysdomain.com'}]>
对于使用all()和不使用all()获取到的都是列表的形式,而如果用first()取到的是一个字典
user_group_obj = UserGroup.objects.filter(host__hostname='dev1.whysdomain.com').values('host__hostname').first()
print user_group_obj
获取结果
{u'host__hostname': u'dev1.whysdomain.com'}
对于反向查找,也就是多的一方会有以下操作
创建了一个新的usergroup
UserGroup.objects.create(caption='test')
user_group_obj = UserGroup.objects.all().values('caption','host__hostname')
print user_group_obj
获取结果
<QuerySet [{u'caption': u'ops', u'host__hostname': u'ops1.whysdomain.com'}, {u'caption': u'ops', u'host__hostname': u'ops2.whysdomain.com'}, {u'caption': u'ops', u'host__hostname': u'ops3.whysdomain.com'}, {u'caption': u'dev', u'host__hostname': u'dev1.whysdomain.com'}, {u'caption': u'dev', u'host__hostname': u'dev2.whysdomain.com'}, {u'caption': u'test', u'host__hostname': None}]>
对于没有可以对应的test,对应查出来的hostname为None
多对多操作
添加多对多数据
如果只是简单的使用了ManyToMany的方法创建多对多,是没有第三张表这个类的,也就无法使用其create方法,当然如果是自行创建或者使用了thought字段关联了是可以使用create的。
也就是没有类对象可以使用
user_info_obj = UserInfo.objects.get(name='why')
print type(user_info_obj)
user_info_objs = UserInfo.objects.all()
print type(user_info_objs)
group_obj = UserGroup.objects.get(caption='ops')
print type(group_obj)
group_objs = UserGroup.objects.all()
print type(group_objs)
return HttpResponse('yes')
获取到的结果为
<class 'app.models.UserInfo'>
<class 'django.db.models.query.QuerySet'>
<class 'app.models.UserGroup'>
<class 'django.db.models.query.QuerySet'>
可以看到user_info_obj
和group_obj
添加数据
user_info_obj = UserInfo.objects.get(name='why')
user_info_objs = UserInfo.objects.all()
group_obj = UserGroup.objects.get(caption='ops')
group_objs = UserGroup.objects.all()
group_obj.user_info.add(user_info_obj)
查询数据
mysql> select * from app_usergroup_user_info;
+----+--------------+-------------+
| id | usergroup_id | userinfo_id |
+----+--------------+-------------+
| 1 | 1 | 1 |
+----+--------------+-------------+
1 row in set (0.00 sec)
删除数据
group_obj.user_info.remove(user_info_obj)
查询数据
mysql> select * from app_usergroup_user_info;
Empty set (0.00 sec)
类中关联字段是在Group类中,可以直接使用user_info
进行添加数据
group_obj.user_info.remove(*user_info_objs)
查询数据
mysql> select * from app_usergroup_user_info;
+----+--------------+-------------+
| id | usergroup_id | userinfo_id |
+----+--------------+-------------+
| 2 | 1 | 1 |
| 3 | 1 | 2 |
+----+--------------+-------------+
2 rows in set (0.00 sec)
同理删除数据
group_obj.user_info.remove(*user_info_objs)
mysql> select * from app_usergroup_user_info;
Empty set (0.00 sec)
对于userinfo就需要“表明_set”来进行添加数据
user_info_obj.usergroup_set.add(*group_objs)
查询数据
mysql> select * from app_usergroup_user_info;
+----+--------------+-------------+
| id | usergroup_id | userinfo_id |
+----+--------------+-------------+
| 4 | 1 | 1 |
| 5 | 2 | 1 |
| 6 | 3 | 1 |
+----+--------------+-------------+
3 rows in set (0.00 sec)
多对多的操作
添加数据
group_obj.user_info.add(user_info_obj)
group_obj.user_info.add(*user_info_objs)
删除数据
group_obj.user_info.remove(user_info_obj)
group_obj.user_info.remove(*user_info_objs)
添加数据
user_info_obj.usergroup_set.add(group_obj)
user_info_obj.usergroup_set.add(*group_objs)
删除数据
user_info_obj.usergroup_set.remove(group_obj)
user_info_obj.usergroup_set.remove(*group_objs)
获取数据
print group_obj.user_info.all()
print group_obj.user_info.all().filter(id=1)
print user_info_obj.usergroup_set.all()
print user_info_obj.usergroup_set.all().filter(caption='dev')
print user_info_obj.usergroup_set.all().filter(caption='ops')