s14 django高级
Django进阶篇
- 参考博客
Django高级:
https://www.cnblogs.com/wupeiqi/articles/5246483.html
Form:
http://www.cnblogs.com/wupeiqi/articles/6144178.html
Model:
http://www.cnblogs.com/wupeiqi/articles/6216618.html
- Model
# django为使用一种新的方式,
# 即:关系对象映射(Object Relational Mapping,简称ORM)。
# django中遵循 Code Frist 的原则,
# 即:根据代码中定义的类来自动生成数据库表。
# db first & code first
# 根据类自动创建数据库表
# 根据类对数据库表中的数据进行各种操作
- 基本操作
a. 先写类
# app下的models.py
from django.db import models
# app01_userinfo
class UserInfo(models.Model):
# 隐含id列,自增,主键
# 用户名列,字符串类型,指定长度
username = models.CharField(max_length=32)
password = models.CharField(max_length=64)
b. 注册APP
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01',
]
c. 执行命令
# 先注册app
根据类自动创建数据库表
python manage.py makemigrations
python manage.py migrate
d. 其他
- Django默认使用MySQLdb模块链接MySQL
- 修改为pymysql,在project同名文件夹下__init__文件添加代码:
import pymysql
pymysql.install_as_MySQLdb()
- 根据数据库,配置参数:
mysql配置如下:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME':'dbname', # 手动创建数据库
'USER': 'root',
'PASSWORD': 'xxx',
'HOST': '',
'PORT': '',
}
}
- pycharm数据库驱动
C:\Users\jacka\.PyCharm2019.2\config\jdbc-drivers
- 使用navicat链接sqlite
- 右击复制copy path
- nivacat:新建连接- 数据库文件
- views.py
from app01 import models
- admin.py
from django.contrib import admin
from app01 import models
# Register your models here.
admin.site.register(models.UserInfo)
创建 Django 用户:python manage.py createsuperuser
------------------------------------------------
- 字段:
- 字符串类型
- 数字
- 时间
- 二进制
- 自增(primary_key=True)
AutoField(Field)
- int自增列,必须填入参数 primary_key=True
BigAutoField(AutoField)
- bigint自增列,必须填入参数 primary_key=True
注:当model中如果没有自增列,则自动会创建一个列名为id的列
from django.db import models
class UserInfo(models.Model):
# 自动创建一个列名为id的且为自增的整数列
username = models.CharField(max_length=32)
class Group(models.Model):
# 自定义自增列
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
SmallIntegerField(IntegerField):
- 小整数 -32768 ~ 32767
PositiveSmallIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正小整数 0 ~ 32767
IntegerField(Field)
- 整数列(有符号的) -2147483648 ~ 2147483647
PositiveIntegerField(PositiveIntegerRelDbTypeMixin, IntegerField)
- 正整数 0 ~ 2147483647
BigIntegerField(IntegerField):
- 长整型(有符号的) -9223372036854775808 ~ 9223372036854775807
自定义无符号整数字段
class UnsignedIntegerField(models.IntegerField):
def db_type(self, connection):
return 'integer UNSIGNED'
PS: 返回值为字段在数据库中的属性,Django字段默认的值为:
'AutoField': 'integer AUTO_INCREMENT',
'BigAutoField': 'bigint AUTO_INCREMENT',
'BinaryField': 'longblob',
'BooleanField': 'bool',
'CharField': 'varchar(%(max_length)s)',
'CommaSeparatedIntegerField': 'varchar(%(max_length)s)',
'DateField': 'date',
'DateTimeField': 'datetime',
'DecimalField': 'numeric(%(max_digits)s, %(decimal_places)s)',
'DurationField': 'bigint',
'FileField': 'varchar(%(max_length)s)',
'FilePathField': 'varchar(%(max_length)s)',
'FloatField': 'double precision',
'IntegerField': 'integer',
'BigIntegerField': 'bigint',
'IPAddressField': 'char(15)',
'GenericIPAddressField': 'char(39)',
'NullBooleanField': 'bool',
'OneToOneField': 'integer',
'PositiveIntegerField': 'integer UNSIGNED',
'PositiveSmallIntegerField': 'smallint UNSIGNED',
'SlugField': 'varchar(%(max_length)s)',
'SmallIntegerField': 'smallint',
'TextField': 'longtext',
'TimeField': 'time',
'UUIDField': 'char(32)',
BooleanField(Field)
- 布尔值类型
NullBooleanField(Field):
- 可以为空的布尔值
CharField(Field)
- 字符类型
- 必须提供max_length参数, max_length表示字符长度
TextField(Field)
- 文本类型
EmailField(CharField):
- 字符串类型,Django Admin以及ModelForm中提供验证机制
IPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 IPV4 机制
GenericIPAddressField(Field)
- 字符串类型,Django Admin以及ModelForm中提供验证 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以及ModelForm中提供验证 URL
SlugField(CharField)
- 字符串类型,Django Admin以及ModelForm中提供验证支持 字母、数字、下划线、连接符(减号)
CommaSeparatedIntegerField(CharField)
- 字符串类型,格式必须为逗号分割的数字
UUIDField(Field)
- 字符串类型,Django Admin以及ModelForm中提供对UUID格式的验证
FilePathField(Field)
- 字符串,Django Admin以及ModelForm中提供读取文件夹下文件的功能
- 参数:
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 上传图片的宽度保存的数据库字段名(字符串)
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是否可以为空
default -> 默认值
primary_key -> 主键
db_column -> 列名 # 设定表列名
db_index -> 索引
unique -> 唯一索引
unique_for_date -> 只对date索引
unique_for_month
unique_for_year
auto_now -> 创建时,自动生成时间
auto_now_add -> 更新时,自动更新为当前时间
# obj = UserGruop.objects.filter(id=1).update(caption='CEO') # 不支持
# 仅限于:
# obj = UserGruop.objects.filter(id=1).first()
# obj.caption ='CEO'
# obj.save()
choices -> django admin中显示下拉框,避免连表查询
blank -> django admin是否可以为空
verbose_name -> django admin显示字段中文
editable -> django admin是否可以被编辑
error_messages -> 错误信息欠
help_text -> django admin提示
validators -> django form ,自定义错误信息(欠)
- 字段类型验证:
Django Admin:有验证功能
model操作:无验证功能
User.objects.create(username='root',email='asdfasdfasdfasdf')
User.objects.filter(id=1).update(email='666')
- 元信息
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"),
]
# 最左前缀的模式:
# select * from where name='xx'
# select * from where name='xx' and email = 'xx'
# select * from where email = 'xx' # 无法命中索引
# 联合唯一索引
unique_together = (("driver", "restaurant"),)
# admin中显示的表名称
verbose_name
# verbose_name加s
verbose_name_plural
更多:https://docs.djangoproject.com/en/1.10/ref/models/options/
- 多表关系以及参数:
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, # 默认创建第三张表时,数据库中表的名称
- ORM操作
- 基本操作
# 增
#
# 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.exclude(name='seven') # 获取指定条件的数据
# 删
#
# 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() # 修改单条数据
- 进阶操作
# 获取个数
#
# 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")
# range
#
# models.Tb1.objects.filter(id__range=[1, 2]) # 范围bettwen and
# 其他类似
#
# startswith,istartswith, endswith, iendswith,
# 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'))
# SELECT "app01_tb1"."id", COUNT("app01_tb1"."num") AS "c" FROM "app01_tb1" WHERE "app01_tb1"."c1" = 1 GROUP BY "app01_tb1"."id"
# limit 、offset
#
# models.Tb1.objects.all()[10:20]
# 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
#
# from django.db.models import F
# models.Tb1.objects.update(num=F('num')+1)
# 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()
- 其他操作
##################################################################
# PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET #
##################################################################
def all(self)
# 获取所有的数据对象
def filter(self, *args, **kwargs)
# 条件查询
# 条件可以是:参数,字典,Q
def exclude(self, *args, **kwargs)
# 条件查询
# 条件可以是:参数,字典,Q
def select_related(self, *fields)
性能相关:表之间进行join连表操作,一次性获取关联的数据。
model.tb.objects.all().select_related()
model.tb.objects.all().select_related('外键字段')
model.tb.objects.all().select_related('外键字段__外键字段')
def prefetch_related(self, *lookups)
性能相关:多表连表操作时速度会慢,使用其执行多次SQL查询在Python代码中实现连表操作。
# 获取所有用户表
# 获取用户类型表where id in (用户表中的查到的所有用户ID)
models.UserInfo.objects.prefetch_related('外键字段')
from django.db.models import Count, Case, When, IntegerField
Article.objects.annotate(
numviews=Count(Case(
When(readership__what_time__lt=treshold, then=1),
output_field=CharField(),
))
)
students = Student.objects.all().annotate(num_excused_absences=models.Sum(
models.Case(
models.When(absence__type='Excused', then=1),
default=0,
output_field=models.IntegerField()
)))
def annotate(self, *args, **kwargs)
# 用于实现聚合group by查询
from django.db.models import Count, Avg, Max, Min, Sum
v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id'))
# SELECT u_id, COUNT(ui) AS `uid` FROM UserInfo GROUP BY u_id
v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id')).filter(uid__gt=1)
# SELECT u_id, COUNT(ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1
v = models.UserInfo.objects.values('u_id').annotate(uid=Count('u_id',distinct=True)).filter(uid__gt=1)
# SELECT u_id, COUNT( DISTINCT ui_id) AS `uid` FROM UserInfo GROUP BY u_id having count(u_id) > 1
def distinct(self, *field_names)
# 用于distinct去重
models.UserInfo.objects.values('nid').distinct()
# select distinct nid from userinfo
注:只有在PostgreSQL中才能使用distinct进行去重
def order_by(self, *field_names)
# 用于排序
models.UserInfo.objects.all().order_by('-id','age')
def 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'])
def reverse(self):
# 倒序
models.UserInfo.objects.all().order_by('-nid').reverse()
# 注:如果存在order_by,reverse则是倒序,如果多个排序则一一倒序
def defer(self, *fields):
models.UserInfo.objects.defer('username','id')
或
models.UserInfo.objects.filter(...).defer('username','id')
#映射中排除某列数据
def only(self, *fields):
#仅取某个表中的数据
models.UserInfo.objects.only('username','id')
或
models.UserInfo.objects.filter(...).only('username','id')
def using(self, alias):
指定使用的数据库,参数为别名(setting中的设置)
##################################################
# PUBLIC METHODS THAT RETURN A QUERYSET SUBCLASS #
##################################################
def raw(self, raw_query, params=None, translations=None, using=None):
# 执行原生SQL
models.UserInfo.objects.raw('select * from userinfo')
# 如果SQL是其他表时,必须将名字设置为当前UserInfo对象的主键列名
models.UserInfo.objects.raw('select id as nid from 其他表')
# 为原生SQL设置参数
models.UserInfo.objects.raw('select id as nid from userinfo where nid>%s', params=[12,])
# 将获取的到列名转换为指定列名
name_map = {'first': 'first_name', 'last': 'last_name', 'bd': 'birth_date', 'pk': 'id'}
Person.objects.raw('SELECT * FROM some_other_table', translations=name_map)
# 指定数据库
models.UserInfo.objects.raw('select * from userinfo', using="default")
################### 原生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() # fetchall()/fetchmany(..)
def values(self, *fields):
# 获取每行数据为字典格式
def values_list(self, *fields, **kwargs):
# 获取每行数据为元祖
def dates(self, field_name, kind, order='ASC'):
# 根据时间进行某一部分进行去重查找并截取指定内容
# kind只能是:"year"(年), "month"(年-月), "day"(年-月-日)
# order只能是:"ASC" "DESC"
# 并获取转换后的时间
- year : 年-01-01
- month: 年-月-01
- day : 年-月-日
models.DatePlus.objects.dates('ctime','day','DESC')
def datetimes(self, field_name, kind, order='ASC', tzinfo=None):
# 根据时间进行某一部分进行去重查找并截取指定内容,将时间转换为指定时区时间
# kind只能是 "year", "month", "day", "hour", "minute", "second"
# order只能是:"ASC" "DESC"
# tzinfo时区对象
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.UTC)
models.DDD.objects.datetimes('ctime','hour',tzinfo=pytz.timezone('Asia/Shanghai'))
"""
pip3 install pytz
import pytz
pytz.all_timezones
pytz.timezone(‘Asia/Shanghai’)
"""
def none(self):
# 空QuerySet对象
####################################
# METHODS THAT DO DATABASE QUERIES #
####################################
def aggregate(self, *args, **kwargs):
# 聚合函数,获取字典类型聚合结果
from django.db.models import Count, Avg, Max, Min, Sum
result = models.UserInfo.objects.aggregate(k=Count('u_id', distinct=True), n=Count('nid'))
===> {'k': 3, 'n': 4}
def count(self):
# 获取个数
def get(self, *args, **kwargs):
# 获取单个对象
def create(self, **kwargs):
# 创建对象
def bulk_create(self, objs, batch_size=None):
# 批量插入
# batch_size表示一次插入的个数
objs = [
models.DDD(name='r11'),
models.DDD(name='r22')
]
models.DDD.objects.bulk_create(objs, 10)
def get_or_create(self, defaults=None, **kwargs):
# 如果存在,则获取,否则,创建
# defaults 指定创建时,其他字段的值
obj, created = models.UserInfo.objects.get_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 2})
def update_or_create(self, defaults=None, **kwargs):
# 如果存在,则更新,否则,创建
# defaults 指定创建时或更新时的其他字段
obj, created = models.UserInfo.objects.update_or_create(username='root1', defaults={'email': '1111111','u_id': 2, 't_id': 1})
def first(self):
# 获取第一个
def last(self):
# 获取最后一个
def in_bulk(self, id_list=None):
# 根据主键ID进行查找
id_list = [11,21,31]
models.DDD.objects.in_bulk(id_list)
def delete(self):
# 删除
def update(self, **kwargs):
# 更新
def exists(self):
# 是否有结果
-------------------------------------------------------
# 一对多/一对一/多对多
一对一:
外键唯一约束。
一对多:
def func():
return 5
class UserType(models.Model):
name = models.CharField(max_length=32)
class User(models.Model):
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
ForiegnKey(to="UserType",to_field='id',on_delete=models.SET(func))
# delete from user where id=1
# delete from UserType where id=1 # 报错
# UserType.objects.filter(id=1).delete()
# 新版本关联数据默认都删除
# 正向查找
# v = User.objects.all()
# for item in v:
# item.user
# item.pwd
# item.ut.name
# User.objects.all().values('user','ut__name')
# 反向查找
# v= UserType.objects.all()
# for item in v:
# item.name
# item.id
# item.user_set.all()
# related_name='b' ==> item.b.all()
# related_query_name='a' ==> item.a_set.all()
# UserType.objects.all().values('name','user__pwd')
- 多对多:
a. django创建第三张表
m2m.remove
m2m.add
m2m.set
m2m.clear
m2m.filter()
b. 自定义第三张表(无m2m字段)
自己链表查询
c. 自定义第三张表(有m2m字段)
# 通过m2m字段查操作
# 通过m2m字段 clear
- QuerySet中的方法:
- 返回QuerySet类型(select_related,prefetch_related)
select_related
users = models.User.objects.all().select_related('ut')
for row in users:
print(row.user,row.pwd,row.ut_id)
print(row.ut.name)
print(row.tu.name) # 再发起一次SQL请求
prefetch_related
users = models.User.objects.filter(id__gt=30).prefetch_related('ut','tu')
# select * from users where id > 30
# 获取上一步骤中所有的ut_id=[1,2]
# select * from user_type where id in [1,2]
# select * from user_type where id in [1,2]
for row in users:
print(row.user,row.pwd,row.ut_id)
print(row.ut.name)
- 数据验证(弱)
full_clean进行验证
- 每个字段的正则
- clean钩子
class UserInfo(models.Model):
name=models.CharField(max_length=32)
email = models.EmailField()
# 预留的方法
def clean(self):
from django.core.exceptions import ValidationError
c = UserInfo.objects.filter(name=self.name).count()
if c:
# 主动报错
raise ValidationError(message='用户名已经存在',code='i1')
- 操作表
- 增
models.User.objects.create(name='qianxiaohu',age=18)
dic = {'name': 'xx', 'age': 19}
models.User.objects.create(**dic)
obj = models.User(name='qianxiaohu',age=18) # 也可以传字典
obj.save()
- 删
models.User.objects.filter(id=1).delete()
- 改
models.User.objects.filter(id__gt=1).update(name='alex',age=84)
dic = {'name': 'xx', 'age': 19}
models.User.objects.filter(id__gt=1).update(**dic)
- 查
models.User.objects.all()
models.User.objects.filter(id=1,name='root')
models.User.objects.filter(id__gt=1,name='root')
models.User.objects.filter(id__lt=1)
models.User.objects.filter(id__gte=1)
models.User.objects.filter(id__lte=1)
models.User.objects.filter(id=1,name='root')
dic = {'name': 'xx', 'age__gt': 19}
models.User.objects.filter(**dic)
- 查2
v1 = models.Business.objects.all()
# QuerySet ,内部元素都是对象
v2 = models.Business.objects.all().values('id','caption')
# QuerySet ,内部元素都是字典
v3 = models.Business.objects.all().values_list('id','caption')
# QuerySet ,内部元素都是元组
models.Business.objects.get(id=1)
# 获取到的一个对象,如果不存在就报错
models.Business.objects.filter(id=1).first()
# 对象或者None
外键:
v = models.Host.objects.filter(nid__gt=0)
v[0].b.caption ----> 通过.进行跨表
v1 = models.Host.objects.filter(nid__gt=0)
# for row in v1:
# print(row.nid,row.hostname,row.ip,row.port,row.b_id,row.b.caption,row.b.code,row.b.id,sep='\t')
v2 = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption')
# models.Host.objects.后面跨表用双下划线: "__" ,为普通字符串,取值时为对象
# print(v2)
# QuerySet[{内部为字典},{字典}]
# for row in v2:
# print(row['nid'],row['hostname'],row['b_id'],row['b__caption'])
v3 = models.Host.objects.filter(nid__gt=0).values_list('nid','hostname','b_id','b__caption')
class UserType(models.Model):
caption = models.CharField(max_length=32)
# id caption
class User(models.Model):
age = models.IntergerFiled()
name = models.CharField(max_length=10)#字符长度
# user_type_id = models.IntergerFiled() # 约束
user_type = models.ForeignKey("UserType",to_field='id') # 约束
# name age user_type_id
# models.tb.object.create(name='root', user_group_id=1)
# userlist = models.tb.object.all()
for row in userlist:
row.id
row.user_group_id
row.user_group.caption
# user = User.objects.get(id=1) # 得到单个对象
- 序号相关
{% for row in v1 %}
<td>{{ forloop.counter}}</td> # 序号从1开始
<td>{{ forloop.counter0}}</td> # 序号从0开始
<td>{{ forloop.revcounter}}</td> # 倒序从1开始
<td>{{ forloop.revcounter0}}</td> # 倒序从0开始
<td>{{ forloop.last}}</td> # 是否最后一个
<td>{{ forloop.firs}}</td> # 是否第一个
<td>{{ forloop.parentloop}}</td> # 如嵌套,显示父循环次数
{%endfor%}
- 多对多:
- 自定义关系表
class Host(models.Model):
nid = models.AutoField(primary_key=True)
hostname = models.CharField(max_length=32,db_index=True)
ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
port = models.IntegerField()
b = models.ForeignKey(to="Business", to_field='id')
class Application(models.Model):
name = models.CharField(max_length=32)
class HostToApp(models.Model):
hobj = models.ForeignKey(to='Host',to_field='nid')
aobj = models.ForeignKey(to='Application',to_field='id')
# app01_hosttoapp 表 id,aobj_id,hobj_id
# HostToApp.objects.create(hobj_id=1,aobj_id=2)
- 自动创建关系表
class Host(models.Model):
nid = models.AutoField(primary_key=True)
hostname = models.CharField(max_length=32,db_index=True)
ip = models.GenericIPAddressField(protocol="ipv4",db_index=True)
port = models.IntegerField()
b = models.ForeignKey(to="Business", to_field='id')
class Application(models.Model):
name = models.CharField(max_length=32)
r = models.ManyToManyField("Host")
# app01.application_r 表 id,application_id,host_id
# 无法直接对第三张表进行操作
obj = Application.objects.get(id=1)
obj.name
- 第三张表操作
obj.r.add(1)
obj.r.add(2)
obj.r.add(2,3,4)
obj.r.add(*[1,2,3,4])
obj.r.remove(1)
obj.r.remove(2,4)
obj.r.remove(*[1,2,3])
obj.r.clear()
obj.r.set([3,5,7])
# objects后的都可以使用
obj.r.all()
# 所有相关的主机对象“列表” QuerySet
obj.r.all()
- FORM
---------------------------------------
- Django的Form主要具有一下几大功能:
生成HTML标签
验证用户数据(显示错误信息)
HTML Form提交保留上次提交数据
初始化页面显示内容
- 小试牛刀
1、创建Form类
from django.forms import Form
from django.forms import widgets
from django.forms import fields
class MyForm(Form):
user = fields.CharField(
widget=widgets.TextInput(attrs={'id': 'i1', 'class': 'c1'})
)
gender = fields.ChoiceField(
choices=((1, '男'), (2, '女'),),
initial=2,
widget=widgets.RadioSelect
)
city = fields.CharField(
initial=2,
widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
)
pwd = fields.CharField(
widget=widgets.PasswordInput(attrs={'class': 'c1'}, render_value=True)
)
2、View函数处理
from django.shortcuts import render, redirect
from .forms import MyForm
def index(request):
if request.method == "GET":
obj = MyForm()
return render(request, 'index.html', {'form': obj})
elif request.method == "POST":
obj = MyForm(request.POST, request.FILES)
if obj.is_valid():
values = obj.clean()
print(values)
else:
errors = obj.errors
print(errors)
return render(request, 'index.html', {'form': obj})
else:
return redirect('http://www.google.com')
3、生成HTML
<form action="/" method="POST" enctype="multipart/form-data">
<p>{{ form.user }} {{ form.user.errors }}</p>
<p>{{ form.gender }} {{ form.gender.errors }}</p>
<p>{{ form.city }} {{ form.city.errors }}</p>
<p>{{ form.pwd }} {{ form.pwd.errors }}</p>
<input type="submit"/>
</form>
1、Django内置字段如下:
Field
required=True, 是否允许为空
widget=None, HTML插件
label=None, 用于生成Label标签或显示内容
initial=None, 初始值
help_text='', 帮助信息(在标签旁边显示)
error_messages=None, 错误信息 {'required': '不能为空', 'invalid': '格式错误'}
show_hidden_initial=False, 是否在当前插件后面再加一个隐藏的且具有默认值的插件(可用于检验两次输入是否一直)
validators=[], 自定义验证规则
localize=False, 是否支持本地化
disabled=False, 是否可以编辑
label_suffix=None Label内容后缀
CharField(Field)
max_length=None, 最大长度
min_length=None, 最小长度
strip=True 是否移除用户输入空白
IntegerField(Field)
max_value=None, 最大值
min_value=None, 最小值
FloatField(IntegerField)
...
DecimalField(IntegerField)
max_value=None, 最大值
min_value=None, 最小值
max_digits=None, 总长度
decimal_places=None, 小数位长度
BaseTemporalField(Field)
input_formats=None 时间格式化
DateField(BaseTemporalField) 格式:2015-09-01
TimeField(BaseTemporalField) 格式:11:12
DateTimeField(BaseTemporalField)格式:2015-09-01 11:12
DurationField(Field) 时间间隔:%d %H:%M:%S.%f
...
RegexField(CharField)
regex, 自定制正则表达式
max_length=None, 最大长度
min_length=None, 最小长度
error_message=None, 忽略,错误信息使用 error_messages={'invalid': '...'}
EmailField(CharField)
...
FileField(Field)
allow_empty_file=False 是否允许空文件
ImageField(FileField)
...
注:需要PIL模块,pip3 install Pillow
以上两个字典使用时,需要注意两点:
- form表单中 enctype="multipart/form-data"
- view函数中 obj = MyForm(request.POST, request.FILES)
URLField(Field)
...
BooleanField(Field)
...
NullBooleanField(BooleanField)
...
ChoiceField(Field)
...
choices=(), 选项,如:choices = ((0,'上海'),(1,'北京'),)
required=True, 是否必填
widget=None, 插件,默认select插件
label=None, Label内容
initial=None, 初始值
help_text='', 帮助提示
ModelChoiceField(ChoiceField)
... django.forms.models.ModelChoiceField
queryset, # 查询数据库中的数据
empty_label="---------", # 默认空显示内容
to_field_name=None, # HTML中value的值对应的字段
limit_choices_to=None # ModelForm中对queryset二次筛选
ModelMultipleChoiceField(ModelChoiceField)
... django.forms.models.ModelMultipleChoiceField
TypedChoiceField(ChoiceField)
coerce = lambda val: val 对选中的值进行一次转换
empty_value= '' 空值的默认值
MultipleChoiceField(ChoiceField)
...
TypedMultipleChoiceField(MultipleChoiceField)
coerce = lambda val: val 对选中的每一个值进行一次转换
empty_value= '' 空值的默认值
ComboField(Field)
fields=() 使用多个验证,如下:即验证最大长度20,又验证邮箱格式
fields.ComboField(fields=[fields.CharField(max_length=20), fields.EmailField(),])
MultiValueField(Field)
PS: 抽象类,子类中可以实现聚合多个字典去匹配一个值,要配合MultiWidget使用
SplitDateTimeField(MultiValueField)
input_date_formats=None, 格式列表:['%Y--%m--%d', '%m%d/%Y', '%m/%d/%y']
input_time_formats=None 格式列表:['%H:%M:%S', '%H:%M:%S.%f', '%H:%M']
FilePathField(ChoiceField) 文件选项,目录下文件显示在页面中
path, 文件夹路径
match=None, 正则匹配
recursive=False, 递归下面的文件夹
allow_files=True, 允许文件
allow_folders=False, 允许文件夹
required=True,
widget=None,
label=None,
initial=None,
help_text=''
GenericIPAddressField
protocol='both', both,ipv4,ipv6支持的IP格式
unpack_ipv4=False 解析ipv4地址,如果是::ffff:192.0.2.1时候,可解析为192.0.2.1, PS:protocol必须为both才能启用
SlugField(CharField) 数字,字母,下划线,减号(连字符)
...
UUIDField(CharField) uuid类型
...
2、Django内置插件:
TextInput(Input)
NumberInput(TextInput)
EmailInput(TextInput)
URLInput(TextInput)
PasswordInput(TextInput)
HiddenInput(TextInput)
Textarea(Widget)
DateInput(DateTimeBaseInput)
DateTimeInput(DateTimeBaseInput)
TimeInput(DateTimeBaseInput)
CheckboxInput
Select
NullBooleanSelect
SelectMultiple
RadioSelect
CheckboxSelectMultiple
FileInput
ClearableFileInput
MultipleHiddenInput
SplitDateTimeWidget
SplitHiddenDateTimeWidget
SelectDateWidget
常用选择插件
# 单radio,值为字符串
# user = fields.CharField(
# initial=2,
# widget=widgets.RadioSelect(choices=((1,'上海'),(2,'北京'),))
# )
# 单radio,值为字符串
# user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
# initial=2,
# widget=widgets.RadioSelect
# )
# 单select,值为字符串
# user = fields.CharField(
# initial=2,
# widget=widgets.Select(choices=((1,'上海'),(2,'北京'),))
# )
# 单select,值为字符串
# user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
# initial=2,
# widget=widgets.Select
# )
# 多选select,值为列表
# user = fields.MultipleChoiceField(
# choices=((1,'上海'),(2,'北京'),),
# initial=[1,],
# widget=widgets.SelectMultiple
# )
# 单checkbox
# user = fields.CharField(
# widget=widgets.CheckboxInput()
# )
# 多选checkbox,值为列表
# user = fields.MultipleChoiceField(
# initial=[2, ],
# choices=((1, '上海'), (2, '北京'),),
# widget=widgets.CheckboxSelectMultiple
# )
在使用选择标签时,需要注意choices的选项可以从数据库中获取,
但是由于是静态字段 ***获取的值无法实时更新***,那么需要自定义构造方法从而达到此目的。
方式一:
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
class MyForm(Form):
user = fields.ChoiceField(
# choices=((1, '上海'), (2, '北京'),),
initial=2,
widget=widgets.Select
)
def __init__(self, *args, **kwargs):
super(MyForm,self).__init__(*args, **kwargs)
# self.fields['user'].widget.choices = ((1, '上海'), (2, '北京'),)
# 或
self.fields['user'].widget.choices = models.Classes.objects.all().value_list('id','caption')
方式二:
使用django提供的ModelChoiceField和ModelMultipleChoiceField字段来实现
from django import forms
from django.forms import fields
from django.forms import widgets
from django.forms import models as form_model
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
class FInfo(forms.Form):
authors = form_model.ModelMultipleChoiceField(queryset=models.NNewType.objects.all())
# authors = form_model.ModelChoiceField(queryset=models.NNewType.objects.all())
- 自定义验证规则
方式一:
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
class MyForm(Form):
user = fields.CharField(
validators=[RegexValidator(r'^[0-9]+$', '请输入数字'), RegexValidator(r'^159[0-9]+$', '数字必须以159开头')],
)
方式二:
import re
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.exceptions import ValidationError
# 自定义验证规则
def mobile_validate(value):
mobile_re = re.compile(r'^(13[0-9]|15[012356789]|17[678]|18[0-9]|14[57])[0-9]{8}$')
if not mobile_re.match(value):
raise ValidationError('手机号码格式错误')
class PublishForm(Form):
title = fields.CharField(max_length=20,
min_length=5,
error_messages={'required': '标题不能为空',
'min_length': '标题最少为5个字符',
'max_length': '标题最多为20个字符'},
widget=widgets.TextInput(attrs={'class': "form-control",
'placeholder': '标题5-20个字符'}))
# 使用自定义验证规则
phone = fields.CharField(validators=[mobile_validate, ],
error_messages={'required': '手机不能为空'},
widget=widgets.TextInput(attrs={'class': "form-control",
'placeholder': u'手机号码'}))
email = fields.EmailField(required=False,
error_messages={'required': u'邮箱不能为空','invalid': u'邮箱格式错误'},
widget=widgets.TextInput(attrs={'class': "form-control", 'placeholder': u'邮箱'}))
方法三:自定义方法
from django import forms
from django.forms import fields
from django.forms import widgets
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
class FInfo(forms.Form):
username = fields.CharField(max_length=5,
validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.', 'invalid')], )
email = fields.EmailField()
def clean_username(self):
"""
Form中字段中定义的格式匹配完之后,执行此方法进行验证
:return:
"""
value = self.cleaned_data['username']
if "666" in value:
raise ValidationError('666已经被玩烂了...', 'invalid')
return value
方式四:同时生成多个标签进行验证
from django.forms import Form
from django.forms import widgets
from django.forms import fields
from django.core.validators import RegexValidator
############## 自定义字段 ##############
class PhoneField(fields.MultiValueField):
def __init__(self, *args, **kwargs):
# Define one message for all fields.
error_messages = {
'incomplete': 'Enter a country calling code and a phone number.',
}
# Or define a different message for each field.
f = (
fields.CharField(
error_messages={'incomplete': 'Enter a country calling code.'},
validators=[
RegexValidator(r'^[0-9]+$', 'Enter a valid country calling code.'),
],
),
fields.CharField(
error_messages={'incomplete': 'Enter a phone number.'},
validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid phone number.')],
),
fields.CharField(
validators=[RegexValidator(r'^[0-9]+$', 'Enter a valid extension.')],
required=False,
),
)
super(PhoneField, self).__init__(error_messages=error_messages, fields=f, require_all_fields=False, *args,
**kwargs)
def compress(self, data_list):
"""
当用户验证都通过后,该值返回给用户
:param data_list:
:return:
"""
return data_list
############## 自定义插件 ##############
class SplitPhoneWidget(widgets.MultiWidget):
def __init__(self):
ws = (
widgets.TextInput(),
widgets.TextInput(),
widgets.TextInput(),
)
super(SplitPhoneWidget, self).__init__(ws)
def decompress(self, value):
"""
处理初始值,当初始值initial不是列表时,调用该方法
:param value:
:return:
"""
if value:
return value.split(',')
return [None, None, None]
-----------------------------
- 动态select数据
user_type = fields.ChoiceField(
# choices=[(1,'普通用户'),(2,'超级用户')],
# choices=models.UserType.objects.values_list('id','name'),
# 增加后无法更新,静态字段
choices=[],
widget = widgets.Select
)
user_type2 = fields.CharField(widget=widgets.Select(choices=[]))
# 解决方法二
user_type3 = ModelChoiceField(
empty_label='请选择用户类型',
queryset=models.UserType.objects.all(),
to_field_name='id'
# 依赖 models.py中的 def __str__(self): return self.name
# 否则选项的是对象
)
# 解决方法一(推荐)
def __init__(self,*args,**kwargs):
# 自定义方法,重新获取
super(UserInfoForm,self).__init__(*args,**kwargs)
self.fields['user_type'].choices = models.UserType.objects.values_list('id','name')
self.fields['user_type2'].widget.choices = models.UserType.objects.values_list('id','name')
- 数据验证(强大)
验证顺序:
字段:
正则 ==> clean_fields
整体:
clean ==> _post_clean
源码查找:
is_valid() errors full_clean _clean_fields clean_fields
_clean_form clean
_post_clean
整体错误信息:
‘__all__’ 'NON_FIELD_ERRORS'
代码示例:
class LoginForm(forms.Form):
user =fields.CharField()
pwd =fields.CharField()
# 需整体显示错误信息
def clean_user(self):
c = models.UserInfo.objects.filter(name=self.cleaned_data['user']).count()
if not c:
return self.cleaned_data['user']
else:
raise ValidationError('用户名已经存在',code='xxx')
def clean_pwd(self):
return self.cleaned_data['pwd']
def clean(self):
c = models.UserInfo.objects.filter(name=self.cleaned_data['user'],pwd=self.cleaned_data['pwd']).count()
if c:
return self.clean_data
else:
raise ValidationError('用户名或密码错误')
def _post_clean(self):
pass
- 序列化
关于Django中的序列化主要应用在将数据库中检索的数据返回给客户端用户,
特别的Ajax请求一般返回的为Json格式。
1、serializers
from django.core import serializers
ret = models.BookType.objects.all()
data = serializers.serialize("json", ret)
2、json.dumps
import json
#ret = models.BookType.objects.all().values('caption')
ret = models.BookType.objects.all().values_list('caption')
ret=list(ret)
result = json.dumps(ret)
3、由于json.dumps时无法处理datetime日期,所以可以通过自定义处理器来做扩展,如:
import json
from datetime import date
from datetime import datetime
class JsonCustomEncoder(json.JSONEncoder):
def default(self, field):
if isinstance(field, datetime):
return o.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(field, date):
return o.strftime('%Y-%m-%d')
else:
return json.JSONEncoder.default(self, field)
# ds = json.dumps(d, cls=JsonCustomEncoder)
示例:
# djang 默认序列化
# from django.core import serializers
# ret = models.UserType.objects.all()
# data = serializers.serialize("json", ret)
# return HttpResponse(data)
# 自定制
import json
v = models.UserType.objects.values_list('id','name')
v=list(v)
return HttpResponse(json.dumps(v))
- ErrorDict
- 自定义encoder
- QuerySet
第一种:
from django.core import serializers
v = models.tb.objects.all()
data = serializers.serialize("json", v)
第二种:
如果有时间进行排除处理。
import json
from datetime import date
from datetime import datetime
class JsonCustomEncoder(json.JSONEncoder):
def default(self, field):
if isinstance(field, datetime):
return field.strftime('%Y-%m-%d %H:%M:%S')
elif isinstance(field, date):
return field.strftime('%Y-%m-%d')
else:
return json.JSONEncoder.default(self, field)
v = models.tb.objects.values('id','name','ctime')
v = list(v)
v = json.dumps(v,cls=JsonCustomEncoder)
- FORM 代码
# views.py
from django import forms
from django.forms import widgets
from django.forms import fields
class FM(forms.Form):
# 字段本身只做验证
user = fields.CharField(
error_messages={'required':'用户名不能为空'},
widget= widgets.Textarea(attrs={'class':'c1'}),
label='用户名',
initial='root',
)
pwd =fields.CharField(
max_length=12,
min_length=6,
error_messages={'required':'密码不能为空',
'min_length':'密码长度不能小于6位',
'max_length':'密码长度不能超过12位'},
widget=widgets.PasswordInput
)
email = fields.EmailField(error_messages={'required':'用户名不能为空','invalid':'邮箱格式错误'})
from app01 import models
def fm(request):
if request.method=='GET':
obj = FM()
return render(request,'fm.html',{'obj':obj})
elif request.method =='POST':
# 获取数据,验证
# 成功后获取正确数据,失败显示错误信息
obj =FM(request.POST)
if obj.is_valid():
print(obj.cleaned_data)
# models.Userinfo.bojects.create(**cleaned_data)
else:
# print(obj.errors)
# print(obj.errors.as_json())
# print(obj.errors['user'][0])
return render(request,'fm.html',{'obj':obj})
# html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<form action="/fm/"method="POST">
{% csrf_token %}
<p>{{ obj.user.label }}{{ obj.user }}{{ obj.errors.user.0 }}</p>
<p>{{ obj.pwd }}{{ obj.errors.pwd.0 }}</p>
<p>{{ obj.email }}{{ obj.errors.email.0 }}</p>
{# {{ obj.as_p }}#}
{# {{ obj.as_ul }}#}
{# <table>{{ obj.as_table }}</table>#}
<p><input type="submit"></p>
</form>
</body>
</html>
- 示例(数据库操作补充之QuerySet方法、 Model数据验证以及钩子、动态Select数据、Form内置钩子、Django序列化)
# urls.py
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^index/', test.index),
url(r'^login.html$', account.login),
url(r'^register/', test.register),
]
# views/test.py
from django.shortcuts import render,HttpResponse
# Create your views here.
from app01 import models
def index(request):
# models.UserType.objects.all().values('name','user__pwd')
# models.UserInfo.objects.create(name='root',email='root')
# 没有进行验证
# obj = models.UserInfo(name='alex',email='alex')
# obj.full_clean() # 验证
# obj.save()
# 此方法会验证
from app01.forms import UserInfoForm
if request.method == 'GET':
obj = UserInfoForm()
# obj = UserInfoForm({'user':'alex'})
# obj.fields['user_type'].choices = models.UserType.objects.values_list('id','name')
return render(request, 'index.html',{'obj':obj})
elif request.method=='POST':
obj = UserInfoForm(request.POST,request.FILES)
obj.is_valid()
def register(request):
from app01.forms import RegisterForm
from django.core.exceptions import NON_FIELD_ERRORS
# obj =RegisterForm(request.POST)
# if obj.is_valid():
# obj.cleaned_data
# else:
# obj.errors
# {
# '__all__':[],
# # 'NON_FIELD_ERRORS':[],
# # 整体错误信息在__all__里面
# 'user':[{'code':'required','message':'xxx'}],
# 'pwd':[{'code':'required','message':'xxx'}],
# }
# djang 默认序列化
# from django.core import serializers
# ret = models.UserType.objects.all()
# data = serializers.serialize("json", ret)
# return HttpResponse(data)
# 自定制
import json
v = models.UserType.objects.values_list('id','name')
v=list(v)
return HttpResponse(json.dumps(v))
# views/account.py
from django.shortcuts import render,HttpResponse
from django import forms
from django.forms import fields
from django.forms import widgets
import json
class LoginForm(forms.Form):
# 登陆
username = fields.CharField()
password = fields.CharField(
max_length=64,
min_length=12,
)
from django.core.exceptions import ValidationError
class JsonCustomEncoder(json.JSONEncoder):
def default(self, field):
if isinstance(field,ValidationError):
return {'code':field.code,'message':field.messages}
else:
return json.JSONEncoder.default(self,field)
def login(request):
ret = {'status':True,'error':None,'data':None}
if request.method=='GET':
return render(request,'login.html')
elif request.method=='POST':
obj = LoginForm(request.POST)
if obj.is_valid():
print(obj.clean_data)
else:
# print(obj.errors,type(obj.errors))
# ret['error'] = obj.errors.as_json()
# 前端需要对 arg parse两回才能得到error
from django.forms.utils import ErrorDict
print(type(obj.errors.as_data()))
for k,v in obj.errors.as_data().items():
print(k,v)
from django.core.exceptions import ValidationError
ret['error'] =obj.errors.as_data()
result =json.dumps(ret,cls=JsonCustomEncoder)
return HttpResponse(result)
# models.py
from django.db import models
# Create your models here.
class UserType(models.Model):
name=models.CharField(max_length=32)
def __str__(self):
return self.name
class User(models.Model):
user =models.CharField(max_length=32)
pwd =models.CharField(max_length=64)
ut = models.ForeignKey(to='UserType',
to_field='id',
related_name='b',
related_query_name='a',
limit_choices_to={'id__gt':5}, # admin 中外键筛选
# parent_link=False #欠着
)
# 正向查找
# v = User.objects.all()
# for item in v:
# item.user
# item.pwd
# item.ut.name
# User.objects.all().values('user','ut__name')
# 反向查找
# v= UserType.objects.all()
# for item in v:
# item.name
# item.id
# item.user_set.all()
# related_name='b' ==> item.b.all()
# related_query_name='a' ==> item.a_set.all()
# UserType.objects.all().values('name','user__pwd')
class Blog(models.Model):
site = models.CharField(max_length=32)
m =models.ManyToManyField('Tag',through='B2T',through_fields=['b','t1'])
class Tag(models.Model):
name = models.CharField(max_length=32)
class B2T(models.Model):
b = models.ForeignKey('Blog')
t1 = models.ForeignKey('Tag')
# t2 = models.ForeignKey('Tag')
class UserInfo(models.Model):
name=models.CharField(max_length=32)
email = models.EmailField()
# 预留的方法
def clean(self):
from django.core.exceptions import ValidationError
c = UserInfo.objects.filter(name=self.name).count()
if c:
# 主动报错
raise ValidationError(message='用户名已经存在',code='i1')
# login.html
<form id="fm">
{% csrf_token %}
<p><input type="text"name="username"></p>
<p><input type="password"name="password"></p>
<p><a id = 'submit'>提交</a></p>
</form>
<script src="/static/jquery-1.12.4.js"></script>
<script>
$(function () {
$('#submit').click(function () {
$.ajax({
url:'/login.html',
type:'POST',
data:$('#fm').serialize(),
//自行打包了csrf_token
success: function (arg) {
console.log(arg);
arg = JSON.parse(arg);
console.log(arg);
},
error: function () {
}
})
})
});
</script>
# forms.py
from django import forms
from django.forms import fields
from django.forms import widgets
from django.forms.models import ModelChoiceField,ModelMultipleChoiceField
from app01 import models
class UserInfoForm(forms.Form):
user = fields.CharField(
required= False,
widget=widgets.Textarea(attrs={'class':'c1'})
)
pwd =fields.CharField(
max_length= 12,
widget= widgets.PasswordInput(attrs={'class':'c1'})
)
user_type = fields.ChoiceField(
# choices=[(1,'普通用户'),(2,'超级用户')],
# choices=models.UserType.objects.values_list('id','name'),
# 增加后无法更新,静态字段
choices=[],
widget = widgets.Select
)
user_type2 = fields.CharField(widget=widgets.Select(choices=[]))
# 解决方法二
user_type3 = ModelChoiceField(
empty_label='请选择用户类型',
queryset=models.UserType.objects.all(),
to_field_name='id'
# 依赖 models.py中的 def __str__(self): return self.name
# 否则选项的是对象
)
# 解决方法一(推荐)
def __init__(self,*args,**kwargs):
# 自定义方法,重新获取
super(UserInfoForm,self).__init__(*args,**kwargs)
self.fields['user_type'].choices = models.UserType.objects.values_list('id','name')
self.fields['user_type2'].widget.choices = models.UserType.objects.values_list('id','name')
# 验证:
# 生成html(保留上一次提交的数据)
# 新URL方式操作(Form方式)
# Ajax请求通常不使用Form生成html,只使用验证。
from django.core.exceptions import ValidationError
class RegisterForm(forms.Form):
user =fields.CharField()
email =fields.EmailField()
# 单独字段
def clean_user(self):
c = models.UserInfo.objects.filter(name=self.cleaned_data['user']).count()
if not c:
return self.cleaned_data['user']
else:
raise ValidationError('用户名已经存在',code='xxx')
def clean_email(self):
return self.cleaned_data['email']
class LoginForm(forms.Form):
user =fields.CharField()
pwd =fields.CharField()
# 需整体显示错误信息(用户名是否存在)
def clean_user(self):
c = models.UserInfo.objects.filter(name=self.cleaned_data['user']).count()
if not c:
return self.cleaned_data['user']
else:
raise ValidationError('用户名已经存在',code='xxx')
def clean_pwd(self):
return self.cleaned_data['pwd']
def clean(self):
c = models.UserInfo.objects.filter(name=self.cleaned_data['user'],pwd=self.cleaned_data['pwd']).count()
if c:
return self.clean_data
else:
raise ValidationError('用户名或密码错误')
# 密码两次输入一致,可在此判断
def _post_clean(self):
pass
#index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<p>{{ obj.user }}</p>
<p>{{ obj.pwd }}</p>
<p>{{ obj.user_type }}</p>
<p>{{ obj.user_type2 }}</p>
<p>{{ obj.user_type3 }}</p>
</body>
</html>
- 示例(基本增删改查和用户登陆、删除、查看详细、字段、参数、外键操作、外键增加):
# app01/urls.py
urlpatterns = [
url(r'^login/', views.login),
url(r'^orm/', views.orm),
url(r'^index/', views.index),
url(r'^user_info/', views.user_info),
url(r'^userdetail-(?P<nid>\d+)/', views.user_detail),
url(r'^userdel-(?P<nid>\d+)/', views.user_del),
url(r'^useredit-(?P<nid>\d+)/', views.user_edit),
]
# app01/views.py
def login(request):
# models.UserInfo.objects.create(caption='DBA')
if request.method =='GET':
# GET 必须大写,否则无法进入
return render(request,'login.html')
elif request.method=='POST':
# 数据库中获取数据
u = request.POST.get('user')
p = request.POST.get('pwd')
# obj=models.UserInfo.objects.filter(username=u,password=p)
# obj=models.UserInfo.objects.filter(username=u,password=p).first() # 一个obj ****
# print(obj)
# obj=models.UserInfo.objects.filter(username=u,password=p).count() # 获取个数
obj=models.UserInfo.objects.filter(username=u,password=p).first()
if obj:
return redirect('/cmdb/index/')
else:
return render(request,'login.html')
else:
# put,delete,head,option..
return redirect('/index/')
def index(request):
return render(request,'index.html')
def user_info(request):
if request.method=='GET':
user_list = models.UserInfo.objects.all()
# QuerySet[obj(id,username..),obj,]
# QuerySet[obj(id,username,email,user_group_id,user_group_id(uid,caption)),obj,]
# for row in user_list:
# print(row.id)
# print(row.user_group)
# print(row.user_group.uid)
# print(row.user_group.caption)
# print(user_list.query)
# 查看SQL语句
group_list = models.UserGruop.objects.all()
return render(request,'user_info.html',{'user_list':user_list,'group_list':group_list})
elif request.method == 'POST':
u = request.POST.get('user')
p = request.POST.get('pwd')
models.UserInfo.objects.create(username=u,password=p)
return redirect('/cmdb/user_info/')
# user_list = models.UserInfo.objects.all()
# return render(request,'user_info.html',{'user_list':user_list})
def user_detail(request,nid):
obj=models.UserInfo.objects.filter(id=nid).first()
# models.UserInfo.objects.get(id=nid) # 不存在报错
return render(request,'user_detail.html',{'obj':obj})
def user_del(request,nid):
models.UserInfo.objects.filter(id=nid).delete()
return redirect('/cmdb/user_info/')
def user_edit(request,nid):
if request.method=="GET":
obj=models.UserInfo.objects.filter(id=nid).first()
return render(request,'user_edit.html',{'obj':obj})
elif request.method=="POST":
nid =request.POST.get('id')
u =request.POST.get('username')
p =request.POST.get('password')
print(nid,u,p)
models.UserInfo.objects.filter(id=nid).update(username=u,password=p)
return redirect('/cmdb/user_info/')
from app01 import models
def orm(request):
# 创建
# models.UserInfo.objects.create(username= 'root',password = '123')
# dic={'username':'eric','password':'666'}
# models.UserInfo.objects.create(**dic)
# obj = models.UserInfo(username= 'alex',password = '123')
# obj.save()
# 查
# result = models.UserInfo.objects.all()
# result:QuerySet django提供的[]
# [obj(id,usernmae,password),obj.obj] obj是 UserInfo对象
# result = models.UserInfo.objects.filter(username='root')
# result = models.UserInfo.objects.filter(username='root',password='123')
# for row in result:
# print(row.id,row.username,row.password)
# print(result)
# 删除
# models.UserInfo.objects.all().delete()
# models.UserInfo.objects.filter(id=4).delete()
# models.UserInfo.objects.filter(username='alex').delete()
# 更新
# models.UserInfo.objects.all().update(password='6666')
# models.UserInfo.objects.filter(id=3).update(password='999')
# 一对多
# user_list =models.UserInfo.objects.all()
# models.UserInfo.objects.create(
# username='root1',
# password='123',
# email='root1@eee.com',
# test='dfdfdfd',
# user_group= models.UserInfo.objects.filter(id=1).first()
# )
models.UserInfo.objects.create(
username='root1',
password='123',
email='root1@eee.com',
test='dfdfdfd',
user_group_id = 1,
)
return HttpResponse('orm')
# models.py
from django.db import models
# Create your models here.
# app01_userinfo
class UserGruop(models.Model):
uid = models.AutoField(primary_key=True)
# 创建自增列
caption =models.CharField(max_length=32)
ctime=models.DateField(auto_now_add=True,null=True)
utime=models.DateField(auto_now=True,null=True)
# user_list = UserInfo.objects.all()
# for row in user_list:
# print(row.user_group_id)
# print(row.user_group)
# print(row.user_group.uid)
# print(row.user_group.cption)
class UserInfo(models.Model):
# 自动创建ID列,自增,主键
# 基本类型:字符串、数字、时间、二进制
username = models.CharField(max_length=32,blank=True,verbose_name='用户名')
password = models.CharField(max_length=64,help_text='pwd')
# 长度改小,则数据丢失
email = models.CharField(max_length=60,default='xxxx@164.com')
test = models.EmailField(max_length=19,null=True)
user_group = models.ForeignKey('UserGruop',to_field='uid',default=1)
# 不添加to_field默认使用主键
# 数据库中:user_group_id 数字
# (uid,caption,ctime,utime)
user_type_choices =(
(1,'超级用户'),
(2,'普通用户'),
(3,'普普通用户'),
)
user_type_id = models.IntegerField(choices=user_type_choices,default=1)
# test = models.URLField(max_length=19,null=True)
# test = models.GenericIPAddressField()
# gender = models.CharField(max_length=60,null=True)
# login.html
<form action="/cmdb/login/"method="POST" enctype="multipart/form-data">
<p><input type="text"name="user"placeholder="用户名"></p>
<p><input type="password"name="pwd"placeholder="密码"></p>
<p><input type="submit"value="提交"></p>
</form>
# index.html(含菜单)
<style>body{margin: 0;}
.menu{display: block;padding: 5px;}</style>
<div style="height: 48px;color: white;">welcome</div>
<div>
<div style="position: absolute;top:48px;bottom:0;left: 0;width: 200px;
<a class="menu" href="/cmdb/user_info/">用户管理</a>
<a class="menu" href="/cmdb/user_group/">用户组管理</a>
</div>
<div style="position: absolute;top:48px;left: 210px;bottom:0;right: 0;overflow: auto"></div>
</div>
# user_info.html
<div style="height: 48px;color: white;">welcome</div>
<div>
<div style="position: absolute;top:48px;bottom:0;left: 0;width: 200px;
<a class="menu" href="/cmdb/user_info/">用户管理</a>
<a class="menu" href="/cmdb/user_group/">用户组管理</a>
</div>
<div style="position: absolute;top:48px;left: 210px;bottom:0;right: 0;overflow: auto">
<h3>添加用户</h3>
<form action="/cmdb/user_info/"method="POST">
<input type="text"name="user">
<input type="text"name="pwd">
<select name="group_id" id="">
{% for item in group_list %}
<option value="{{ item.uid }}">{{ item.caption }}</option>
{% endfor %}
</select>
<input type="submit">
</form>
<h3>用户列表</h3>
<ul>
{% for row in user_list %}
<li><a href="/cmdb/userdetail-{{ row.id }}">{{ row.username }}</a> |
<span>{{ row.user_group.caption }}</span>
<a href="/cmdb/userdel-{{ row.id }}/">删除</a>|
<a href="/cmdb/useredit-{{ row.id }}/">编辑</a></li>
{% endfor %}
</ul>
</div>
</div>
# user_detail.html(详细信息)
<div style="height: 48px;color: white;">welcome</div>
<div>
<div style="position: absolute;top:48px;bottom:0;left: 0;width: 200px;
<a class="menu" href="/cmdb/user_info/">用户管理</a>
<a class="menu" href="/cmdb/user_group/">用户组管理</a>
</div>
<div style="position: absolute;top:48px;left: 210px;bottom:0;right: 0;overflow: auto">
<h1>用户详细信息</h1>
<h5>{{ obj.id }}</h5>
<h5>{{ obj.username }}</h5>
<h5>{{ obj.password }}</h5>
</div>
</div>
# user_edit.html
<div style="height: 48px;color: white;">welcome</div>
<div>
<div style="position: absolute;top:48px;bottom:0;left: 0;width: 200px;
<a class="menu" href="/cmdb/user_info/">用户管理</a>
<a class="menu" href="/cmdb/user_group/">用户组管理</a>
</div>
<div style="position: absolute;top:48px;left: 210px;bottom:0;right: 0;overflow: auto">
<h1>编辑用户</h1>
<form action="/cmdb/useredit-{{ obj.id }}/"method="POST">
<input style="display: none" type="text"name="id" value="{{ obj.id }}">
<input type="text"name="username" value="{{ obj.username }}">
<input type="text" name="password" value="{{ obj.password }}">
<input type="submit">
</form>
</div>
</div>
# admin.py
from django.contrib import admin
from app01 import models
# Register your models here.
admin.site.register(models.UserInfo)
- 示例(主机、业务线、应用 获取表单3方式、一对多、多对多操作,ajax)
# urls.py
from app01 import views
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^business$', views.business),
url(r'^host$', views.host),
url(r'^test_ajax', views.test_ajax),
url(r'^app$', views.app),
url(r'^ajax_add_app$', views.ajax_add_app),
]
# models.py
from django.db import models
# Create your models here.
# class Foo(models.Model):
# name = models.CharField(max_length=1)
# row.b.fk.name
class Business(models.Model):
# id
caption=models.CharField(max_length=32)
code = models.CharField(max_length=32,null=True,default='SA')
# fk = models.ForeignKey('Foo')
class Host(models.Model):
nid = models.AutoField(primary_key=True)
hostname= models.CharField(max_length=32,db_index=True)
ip = models.GenericIPAddressField(protocol='ipv4',db_index=True)
port = models.IntegerField()
b = models.ForeignKey(to = 'Business',to_field='id')
# 多对多
class Application(models.Model):
name=models.CharField(max_length=32)
r =models.ManyToManyField('Host')
# app01.application_r 表 id,application_id,host_id
# 手动创建
# class HostToApp(models.Model):
# hobj = models.ForeignKey(to='Host',to_field='nid')
# aobj = models.ForeignKey(to='Application',to_field='id')
# app01_hosttoapp 表 id,aobj_id,hobj_id
# views.py
from django.shortcuts import render,HttpResponse,redirect
from app01 import models
# Create your views here.
def business(request):
v1 = models.Business.objects.all()
# QuerySet[obj(id,caption,code),obj] 内部为对象
v2 = models.Business.objects.all().values('id','caption')
# QuerySet[{'id':1,'caption':'xxx'},{},{}] 内部为列表
v3 = models.Business.objects.values_list('id','caption')
# QuerySet[(1,xxx),(2,yyy)] 内部为元组
return render(request,'business.html',{'v1':v1,'v2':v2,'v3':v3})
"""
def host(request):
v1 = models.Host.objects.filter(nid__gt=0)
# for row in v1:
# print(row.nid,row.hostname,row.ip,row.port,row.b_id,row.b.caption,row.b.code,row.b.id,sep='\t')
v2 = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption')
# models.Host.objects.后面跨表用双下划线: "__" ,为普通字符串,取值时为对象
# print(v2)
# QuerySet[{内部为字典},{字典}]
# for row in v2:
# print(row['nid'],row['hostname'],row['b_id'],row['b__caption'])
v3 = models.Host.objects.filter(nid__gt=0).values_list('nid','hostname','b_id','b__caption')
return render(request,'host.html',{'v1':v1,'v2':v2,'v3':v3})
"""
def host(request):
if request.method =='GET':
v1 = models.Host.objects.filter(nid__gt=0)
v2 = models.Host.objects.filter(nid__gt=0).values('nid','hostname','b_id','b__caption')
v3 = models.Host.objects.filter(nid__gt=0).values_list('nid','hostname','b_id','b__caption')
b_list = models.Business.objects.all()
return render(request,'host.html',{'v1':v1,'v2':v2,'v3':v3,'b_list':b_list})
elif request.method=='POST':
h = request.POST.get('hostname')
i = request.POST.get('ip')
p = request.POST.get('port')
b = request.POST.get('b_id')
models.Host.objects.create(hostname =h,
ip=i,
port=p,
b_id=b)
# return render(request,'host.html')
return redirect('/host')
def test_ajax(request):
import json
ret={'status':True,'error':None,'data':None}
try:
h = request.POST.get('hostname')
i = request.POST.get('ip')
p = request.POST.get('port')
b = request.POST.get('b_id')
if h and len(h) > 5:
models.Host.objects.create(hostname =h,
ip=i,
port=p,
b_id=b)
else:
ret['status'] = False
ret['error'] = 'too lower'
except Exception as e:
ret['status']= False
ret['error'] = '请求错误'
return HttpResponse(json.dumps(ret))
def app(request):
if request.method=='GET':
app_list=models.Application.objects.all()
# for row in app_list:
# print(row.name,row.r.all())
host_list = models.Host.objects.all()
return render(request,'app.html',{'app_list':app_list,'host_list':host_list})
elif request.method == 'POST':
app_name =request.POST.get('app_name')
host_list =request.POST.getlist('host_list')
print(app_name,host_list)
obj= models.Application.objects.create(name=app_name)
# obj 为刚才操作过的
obj.r.add(*host_list)
return redirect('/app')
import json
def ajax_add_app(request):
# 创建ret,待写
ret={'status':True,'error':None,'data':None}
app_name= request.POST.get('app_name')
host_list= request.POST.getlist('host_list')
obj= models.Application.objects.create(name=app_name)
obj.r.add(*host_list)
return HttpResponse(json.dumps(ret))
# business.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
</head>
<body>
<h1>业务线列表(对象)</h1>
<ul>
{% for row in v1 %}
<li>{{ row.id }} - {{ row.caption }} - {{ row.code }}</li>
{% endfor %}
</ul>
<h1>业务线列表(字典)</h1>
<ul>
{% for row in v2 %}
<li>{{ row.id }} - {{ row.caption }}</li>
{% endfor %}
</ul>
<h1>业务线列表(元组)</h1>
<ul>
{% for row in v3 %}
<li>{{ row.0 }} - {{ row.1 }}</li>
{% endfor %}
</ul>
</body>
</html>
# host.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.hide{display: none}
.shade{position: fixed;top:0;right: 0;left: 0;bottom: 0;
background: black;opacity: 0.6;z-index:100; }
.add-modal,.edit-modal{position: fixed;height: 300px;width:400px;top:100px;left: 50% ;
border:1px solid green;z-index: 101;background: white;margin-left: -200px;}
</style>
</head>
<body>
<h1>主机列表(对象)</h1>
<div>
<input id='add_host' type="button"value="添加">
</div>
<table border="1">
<thead>
<tr>
{# <th>主机ID</th>#}
<th>序号</th>
<th>主机名</th>
<th>IP</th>
<th>端口</th>
{# <th>业务线ID</th>#}
<th>业务线名称</th>
<th>操作</th>
{# <th>业务线编码</th>#}
</tr>
</thead>
<tbody>
{# {% for i in v1 %}#}
{% for row in v1 %}
<tr hid="{{ row.nid }}" bid="{{ row.b_id }}">
// 将两个id 放置于此处,待用。
{# <td>{{ row.nid }}</td>#}
<td>{{ forloop.counter}}</td>
<td>{{ row.hostname }}</td>
<td>{{ row.ip }}</td>
<td>{{ row.port }}</td>
{# <td>{{ row.b_id }}</td>#}
<td>{{ row.b.caption }}</td>
<td><a class="edit">编辑</a>|<a class="delete">删除</a></td>
// 实现删除时,可通过Form表单或Ajax操作,Ajax操作时可找到页面当前行,进行删除。
{# <td>{{ row.b.code }}</td>#}
</tr>
{% endfor %}
{# {% endfor %}#}
</tbody>
</table>
<h1>主机列表(字典)</h1>
<table border="1">
<thead>
<tr>
<th>主机名</th>
<th>业务线名称</th>
</tr>
</thead>
<tbody>
{% for row in v2 %}
<tr hid="{{ row.id }}" bid="{{ row.b_id }}">
<td>{{ row.hostname }}</td>
<td>{{ row.b__caption }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<h1>主机列表(元组)</h1>
<table border="1">
<thead>
<tr>
<th>主机名</th>
<th>业务线名称</th>
</tr>
</thead>
<tbody>
{% for row in v3 %}
<tr hid="{{ row.0 }}" bid="{{ row.2 }}">
<td>{{ row.1 }}</td>
<td>{{ row.3 }}</td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="shade hide"></div>
<div class="add-modal hide">
<form id='add_form'action="/host"method="POST">
// 未含验证功能,待Ajax
<div class="group"><input id="host" type="text"placeholder="主机名"name="hostname"></div>
<div class="group"><input id="ip" type="text"placeholder="ip"name="ip"></div>
<div class="group"><input id="port" type="text"placeholder="端口"name="port"></div>
<div class="group"><select name="b_id" id="sel">
{% for op in b_list %}
<option value="{{ op.id }}">{{ op.caption }}</option>
{% endfor %}
</select></div>
<input type="submit"value="提交">
<a id="ajax_submit" style="display: inline-block;padding: 5px;
color: white">Ajax提交</a>
<input id="cancel" type="button"value="取消">
<span id="erro_msg" style="color: red;"></span>
</form>
</div>
<div class="edit-modal hide">
<form id='edit_form'action="/host"method="POST">
<input type="text"name="nid" style="display: none;">
// 额外添加,修改后提交时获取host的id使用
<input type="text"placeholder="主机名"name="hostname">
<input type="text"placeholder="ip"name="ip">
<input type="text"placeholder="端口"name="port">
<select name="b_id" id="sel">
{% for op in b_list %}
<option value="{{ op.id }}">{{ op.caption }}</option>
{% endfor %}
</select>
<a id="ajax_sumit_edit">确认编辑</a>
</form>
</div>
<script src="/static/jquery-1.12.4.js"></script>
<script>
$(function () {
// 模态对话框
$('#add_host').click(function () {
$('.shade,.add-modal').removeClass('hide');
});
$('#cancel').click(function () {
$('.shade,.add-modal').addClass('hide');
})
});
// Ajax 提交
$('#ajax_submit').click(function () {
$.ajax({
url:'/test_ajax',
type:"POST",
{# data:{'hostname':$('#host').val(),#}
{# 'ip':$('#ip').val(),#}
{# 'port':$('#port').val(),#}
{# 'b_id':$('#sel').val()#}
{# },#}
data:$('#add_form').serialize(),
success: function (data) {
var obj =JSON.parse(data);
if (obj.status){
location.reload();
}else{
$("#erro_msg").text(obj.error);
}
}
})
});
// Ajax 编辑
$('.edit').click(function () {
$('.shade,.edit-modal').removeClass('hide');
// 只实现了多选,其他待添加
var bid= $(this).parent().parent().attr('bid');
// ** 获取编辑行修改前的 business 的 id
var nid= $(this).parent().parent().attr('hid');
// @@ 获取当前编辑行host的 id
console.log(bid,nid);
$('#edit_form').find('select').val(bid);
// ** 将 bid 赋值给模态对话框 select标签:选中状态
$('#edit_form').find('input[name="nid"]').val(nid);
// @@ 将当前编辑的host id 赋值给 模态框 额外添加的input框
$.ajax({
url:'',
data:$('#edit_form').serialize()
});
// models.Host.object.filter(nid=nid).update()
});
</script>
</body>
</html>
# app.py
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title></title>
<style>
.host-tag{display: inline-block;padding: 3px;border: 1px solid red;}
</style>
<style>
.hide{display: none}
.shade{position: fixed;top:0;right: 0;left: 0;bottom: 0;
background: black;opacity: 0.6;z-index:100; }
.add-modal,.edit-modal{position: fixed;height: 300px;width:400px;top:100px;left: 50% ;
border:1px solid green;z-index: 101;background: white;margin-left: -200px;}
</style>
</head>
<body>
<h1>****** 多对多*******</h1>
<h1>应用列表</h1>
<div>
<input id='add_app' type="button"value="add">
</div>
<table border="1">
<thead>
<tr>
<td>应用名称</td>
<td>应用主机列表</td>
</tr>
</thead>
<tbody>
{% for app in app_list %}
<tr aid = '{{ app.id }}'>
// 定制aid 为编辑使用
<td>{{ app.name }}</td>
<td>
{% for host in app.r.all %}
<span class='host-tag' hid="{{ host.nid }}">{{ host.hostname }}</span>
// hid="{{ host.nid }} 额外添加,为编辑使用准备
// 后面添加 删除 图标,进行删除,待写。
{% endfor %}
</td>
<td><a class="edit">编辑</a></td>
</tr>
{% endfor %}
</tbody>
</table>
<div class="shade hide"></div>
<div class="add-modal hide">
<form id='add_form' action="/app" method="POST">
<div class="group"><input id="app_name" type="text"placeholder="应用名称"name="app_name"></div>
<div class="group"><select name="host_list" id="host_list" multiple>
{% for op in host_list %}
<option value="{{ op.nid }}">{{ op.hostname }}</option>
{% endfor %}
</select></div>
<input type="submit"value="提交">
<input id="add_submit_ajax" type="button"value="ajax提交">
</form>
</div>
<div class="edit-modal hide">
<form id='edit_form' action="/host" method="POST">
<input type="text"name="nid" style="display: none;">
<input type="text"placeholder="应用名称"name="app">
<select name="host_list" multiple>
{% for op in host_list %}
<option value="{{ op.nid }}">{{ op.hostname }}</option>
{% endfor %}
</select>
<a id="ajax_sumit_edit">编辑</a>
</form>
</div>
<script src="/static/jquery-1.12.4.js"></script>
<script>
$(function () {
$('#add_app').click(function () {
$('.shade,.add-modal').removeClass('hide');
});
$('#cancel').click(function () {
$('.shade,.add-modal').addClass('hide');
});
$("#add_submit_ajax").click(function () {
$.ajax({
url:'/ajax_add_app',
{# data:{'user':123,'host_list':[1,2,3,4]},#}
data:$('#add_form').serialize(),
type:'POST',
dataType:JSON,
traditional:true,
// 添加后才能处理: []
success: function (obj) {
console.log(obj);
// 拿到ret进行判断、跳转,待写。
},
error: function () {
}
});
});
// 编辑功能
$('.edit').click(function () {
$('.edit-modal,.shade').removeClass('hide');
// 仅做了 主机 多选项,其他待写。
var hid_list=[];
$(this).parent().prev().children().each(function () {
// 对span标签循环,获取hid
{# var text= $(this).text();#} // 获取主机名
var hid = $(this).attr('hid'); // 获取value
hid_list.push(hid)
});
//select 设置多选后,value 是个列表
$('#edit_form').find('select').val(hid_list);
//发送到后台 obj=model.Application.objects.get(id=aid)
// obj.name= New name
// obj.save()
// obj.r.set([1,2,3,])
});
});
</script>
</body>
</html>