模型使⽤步骤:
Django默认使⽤的是sqlite,但在⽣产环境中⼀般会⽤mysql、postgrsql、oracle
等关系型数据库。本文简单介绍在django中对于关系型数据库的一些用法。
在虚拟开发环境中,安装mysql的数据库驱动
pip install mysqlclient
在项⽬的 settings.py ⽂件中找到 DATABASES 配置项,将其信息修改为:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql', #mysql数据库引擎
'NAME': 'test', #数据库名
'HOST':'localhost', #数据库服务器地址
'USER': 'test', #mysql数据库⽤户名
'PASSWORD': 'test123', #密码
'PORT':3306, #端⼝号,可选
}
}
ORM与数据库的关系:通常,一个模型(model)映射到一个数据库表。
1. 在models.py中定义模型类
from django.db import models
from django.utils import timezone
#⽤户类
class User(models.Model):
uid = models.AutoField(primary_key=True) #⾃增主键
uname = models.CharField(max_length=60)
password = models.CharField(max_length=32)
user_type = ((1,'超级管理员'),(2,'普通⽤户'))
type = models.IntegerField(default=2,choices=user_type)
regtime = models.DateTimeField(default=timezone.now) #缺省值是当
前时间
ip = models.IntegerField(null=True)
login_type = ((1,'允许登录'),(2,'禁⽌登录')) #⽤户⾃定义类型对应
mysql的enum类型
allowed = models.IntegerField(default=1,choices=login_type)
email = models.CharField(max_length=100,null=True)
memo = models.CharField(max_length=1000,null=True)
class Meta:
db_table = 'user' #表名
2. 激活模型
python manage.py makemigrations
python manage.py migrate
3. 生成表结构
CREATE TABLE `user` (
`uid` int(11) NOT NULL AUTO_INCREMENT,
`uname` varchar(60) NOT NULL,
`password` char(32) NOT NULL,
`type` enum('超级管理员','普通⽤户') DEFAULT '普通⽤户',
`regtime` datetime DEFAULT NULL,
`ip` int(11) DEFAULT NULL,
`allowed` enum('允许登录','禁⽌登录') DEFAULT '允许登录',
`email` varchar(100) DEFAULT NULL,
`memo` varchar(1000) DEFAULT NULL,
PRIMARY KEY (`uid`)
)
注意:任何对字段或表的修改都需要重新迁移
python manage.py inspectdb > App/models.py
增删改查
>>> from app.models.User import User #导⼊User模型
>>> from hashlib import md5
>>> # 实例化⼀个新对象
>>> user = User(uname='admin',password=md5(b'123').hexdigest())
>>> user.save() # insert 插⼊数据库
>>> # 插⼊
>>> User.objects.create(username='hello',password='111')
>>> # 查询
>>> u1 = User.objects.get(pk=1)
>>> # 删除
>>> u1.delete()
>>> # 修改
>>> (单条记录)
>>> u2 = User.objects.get(pk=2)
>>> u2.uname = 'admin'
>>> u2.save()
>>> (多条记录)
>>> users = User.objects.all()
>>> users.update(password=md5(b'345').hexdigest())
名称 | 说明 |
---|---|
IntegerField | 整数 |
AutoField | ⼀个根据实际Id⾃动增⻓的IntegerField(通常不指定⾃动⽣成) |
CharField | 字符串,默认的表单样式是TextInput |
DateTimeField | 使⽤Python的datetime.datetime实例表示的日期和时间 |
BooleanField | true/false 字段,此字段的默认表单控制是CheckboxInput |
unique | 如果 True ,该字段在整个表格中必须是唯⼀的。 |
primary_key | 如果 True ,此字段是模型的主键。 |
default | 默认值,当前字段如果不给值则执⾏默认值 |
db_table | 数据库中的表名 |
abstract | 当设置为True时,说明当前模型是抽象基类 |
类.objects.all() | 返回表中所有数据 |
类.objects.get() | 返回一个符合的数据 |
类.objects.filter() | 返回符合条件的数据模型 |
类.objects.exclude() | 返回不符合条件的数据模型 |
类.objects.order_by() | 对查询结果集进⾏排序模型 |
类.objects.values() | 返回⼀个Queryset,其中每个对象为⼀个字典 |
类.objects.exists() | bool , 判断查询的数据是否存在 |
类.objects.count() | int ,返回查询集中对象的数⽬ |
contains | 模糊查询,等价like ‘%值%’ |
startswith | 以…开头, 例如 uname__startswith = ‘a’ |
endswith | 以…结尾 |
isnull | 判空(等价 = None) |
regex | 正则匹配 |
raw | 原始sql , 例如 users = User.objects.raw(“select * from user”) |
__gt | 大于 |
__gte | 大于等于 |
__lt | 小于 |
__lte | 小于等于 |
数据库中有三种关联类型:
为了正常显示,必须把数据库的编码格式设置成'utf-8',具体的方法步骤如下:
1 win + R ,输入cmd;
2 mysql -u root -p
3 输入密码;
4 SHOW VARIABLES LIKE 'character%';
5 set character_set_server = utf8;
一个学生(学生表是主表)有一个档案(档案表是从表),一个档案属于一个学生。
class Student(models.Model):
sno = models.CharField(max_length=6, primary_key=True)
sname = models.CharField(max_length=100, null=False)
ssex = models.CharField(max_length=2, default='男', null=True)
sage = models.IntegerField(null=True)
sclass = models.CharField(max_length=10, null=True)
def __str__(self):
return "no:{},name:{}".format(self.sno, self.sname)
class Meta:
db_table = 'student'
class Archives(models.Model):
idcard = models.CharField(max_length=18, unique=True)
address = models.CharField(max_length=200, null=True)
# on_delete=models.CASCADE 级联删除,删除学⽣会连同档案⼀块删除
'''
CASECADE 默认,默认级联删除数据
PROTECT 保护模式,当从表中存在级联记录的时候,删除主表记录会抛
出保护异常,从表中不存在级联数据的时候,是允许删除的
SET_XXX
NULL 外键字段本身必须允许为空
DEFAULT 外键字段本身有默认值
DO_NOTHING 什么都不做
'''
student = models.OneToOneField(Student, on_delete=models.CASCADE)
def __str__(self):
return "{},{}".format(self.idcard, self.address)
class Meta:
db_table = 'archives'
def add_stu(request):
student = Student()
student.sno = '220401'
student.sname = '南星'
student.sage = 23
student.save()
return HttpResponse("增加学生")
def add_arc(request):
stu = Student.objects.get(pk='220402')
arc = Archives()
arc.idcard = '1234565467654'
arc.student = stu #学⽣对象必须已经保存到数据库,否则错误
arc.save()
return HttpResponse("增加档案")
def del_stu(request):
student = Student.objects.get(pk='220403')
student.delete()
def find_stu(request):
student = Student.objects.first()
print(student)
archive = student.archives
print(archive)
def find_arc(request):
archive = Archives.objects.first()
student = archive.student
return HttpResponse(student)
def loopup(request):
student = Student.objects.get(archives__pk=1)
archive = Archives.objects.get(student__sno='220404')
return ...
⼀个出版社可以出版多本书,⼀本书只能被⼀个出版社出版。将主表中的主键并到从表中做外键。
class Book(models.Model):
bname = models.CharField(max_length=200, null=True)
# 多对⼀模型通过ForeignKey表示多对⼀
# 如果publisher定义在book之后,第⼀个参数应该⽤字符串'Publisher'(出版社类)
publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE,
null=True,
db_column='pid',
# 表中字段名
related_name='books')
# 通过出版社查图书时使⽤的关系名
def __str__(self):
return self.bname
class Meta:
db_table = 'book'
⼀个买家可以购买多件商品,⼀件商品可以被多个买家购买。
# 方法一:ManyToaManyField
class Goods(models.Model):
gname = models.CharField(max_length=100)
price = models.FloatField()
buyer = models.ManyToManyField(Buyer)
# 这种写法⾃动⽣成第三张表,但我们⽆法直接控制
# Buyer为卖家类
def __str__(self):
return self.gname + " " + str(self.price)
# 方法二:正向一对多和反向一对多,就是多对多
class Orders(models.Model):
buyer = models.ForeignKey(Buyer, on_delete=models.CASCADE, db_column='bid')
goods = models.ForeignKey('Goods', on_delete=models.CASCADE, db_column='gid')
num = models.Integer(default=1)
class Goods(models.Model):
gname = models.CharField(max_length=100)
price = models.FloatField()
buyer = models.ManyToManyField(Buyer, through='Orders')
class Parent(models.Model):
...
# 可以在models中添加Meta,指定是否抽象,然后进⾏继承
class Meta:
abstract = True/False
# abstract=True:将⽗类抽象化
# 抽象化的⽗类不会再数据库⽣成表
# ⼦类会将⽗类中的通⽤数据,复制到⼦表中
class Child(Parent):
...
Django在内部集成了一个表单模块,专门帮助我们快速处理表单相关的内容。Django的表单模块给我们提供了三个主要功能:
Form相关的对象包括:
名称 | 说明 | 示例 |
---|---|---|
cleaned_data(字典) | 表单中验证通过的⼲净数据 | form.cleaned_data form.cleaned_data.get(‘username’) |
changed_data | 有变化的字段的列表 | form.changed_data |
fields(字典) | 表单中的字段属性 | form.fiedls[‘username’] |
is_bound | 表单是否绑定数据 | form.is_bound |
errors(字典) | 错误信息 | form.errors |
is_valid() | 表单中数据是否验证通过,通过返回True,否则返回False | form.is_valid() |
has_changed() | 检查表单数据是否已从初始数据更改 | form.has_changed() |
errors.as_json(escape_html=False) | 返回JSON序列化后的错误信息字典 | form.errors.as_json() |
models.py
from django.db import models
class Info(models.Model):
name = models.CharField(max_length=64)
sex = models.CharField(max_length=64)
birthday = models.CharField(max_length=64)
age = models.CharField(max_length=64)
qualification = models.CharField(max_length=64)
job = models.CharField(max_length=64)
email = models.CharField(max_length=64, default='')
class Hobby(models.Model):
item = models.CharField(max_length=64)
form.py
from django import forms
form App import models
from django.core.exceptions import ValidationError
class InfoForm(forms.Form):
def validate_name(value):
try:
models.Info.objects.get(name=value)
raise ValidationError('%s 的信息已经存在!' % value)
except models.Info.DoesNotExist:
pass
sex_choice = ((0, '男'),(1, '女'))
# select的数据可以像这样写,也可以在另外⼀张表中动态去拿
name = forms.CharField(validators=[validate_name], label='姓名',
error_messages={'required': '必填'})
age = forms.CharField(label='年龄', error_messages={'required': '必填'})
# sex = forms.CharField(label='性别',error_messages={'required':'必填'},)
sex = forms.IntegerField(
widget=forms.widgets.Select(
choices=sex_choice, attrs={'class': 'setform2'}
)
)
birthday = forms.CharField(label='生日', error_messages={'required': '必填'})
qualification = forms.CharField(
label='学历',
error_messages={'required': '必填'},
widget=forms.TextInput(
attrs={'class': 'formset', 'placeholder': '本科'}
)
)
email = forms.EmailField(max_length=100, min_length=10)
job = forms.CharField(label='工作', error_messages={'required': '必填'})
def __init__(self, *args, **kwargs):
super(Info_form, self).__init__(*args, **kwargs)
self.fields['hobby'] = forms.CharField(
widget=forms.widgets.Select(
choices=models.Hobby.objects.values_list('id', 'item')
)
)
views.py
from django.shortcuts import render, HttpResponse
def add_info(req):
if req.method == 'POST':
Info_form_obj = Info_form(req.POST)
if Info_form_obj.is_valid():
Info.objects.create(name=Info_form_obj.cleaned_data['name']),
age = Info_form_obj.cleaned_data['age'],
sex = Info_form_obj.cleaned_data['sex'],
birthday = Info_form_obj.cleaned_data['birthday'],
qualification = Info_form_obj.cleaned_data['qualification'],
job = Info_form_obj.cleaned_data['job']
return HttpResponse('添加成功')
else:
error_obj = Info_form_obj.errors
print('**************')
print(type(error_obj)) # class
print(error_obj['name'][0]) # 必填
print(error_obj.get('age'))
# - 必填
return render(req, 'add_info.html', {'form_obj': Info_form_obj})
add_info.html
DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>添加个人信息title>
<style>
.formset{
color: rebeccapurple;
border: dashed cadetblue;
}
style>
head>
<body>
<form action="{% url 'add_info' %}" method="post">
<p>姓名{{ form_obj.name }}{{ error_obj.name.0 }}p>
<p>年龄{{ form_obj.age }}{{ error_obj.age.0 }}p>
<p>生日{{ form_obj.birthday }}{{ error_obj.birthday.0 }}p>
<p>工作{{ form_obj.job }}<span>{{ error_obj.job }}span>p>
<p>学历{{ form_obj.qualification }}<span>{{ error_obj.qualification }}span>p>
<p>性别{{ form_obj.sex }}<span>{{ error_obj.sex }}span>p>
<p>邮箱{{ form_obj.email }}<span>{{ error_obj.emial }}span>p>
<p>爱好{{ form_obj.hobby }}<span>{{ error_obj.hobby }}span>p>
{{ form_obj.as_p }}
<input type="submit" value="提交"><br>
{% csrf_token %}
form>
body>
html>
更多详细用法可参考 Django官网文档 。