Django——ORM数据库的配置(以MySQL为例)、模型对应关系、Form表单

一、ORM数据库的配置与使用

模型使⽤步骤:

  1. 配置数据库
  2. models.py定义模型类
  3. 激活模型
  4. 使⽤模型

Django默认使⽤的是sqlite,但在⽣产环境中⼀般会⽤mysql、postgrsql、oracle
等关系型数据库。本文简单介绍在django中对于关系型数据库的一些用法。


1.1 数据库配置

在虚拟开发环境中,安装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, #端⼝号,可选
 	}
}

1.2 ORM——对象关系映射(Oject Relational Mapping)

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

1.3 模型的使用

增删改查

>>> 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())

1.4 其他用法

名称说明
IntegerField整数
AutoField⼀个根据实际Id⾃动增⻓的IntegerField(通常不指定⾃动⽣成)
CharField字符串,默认的表单样式是TextInput
DateTimeField使⽤Python的datetime.datetime实例表示的日期和时间
BooleanFieldtrue/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;

2.1 一对一

一个学生(学生表是主表)有一个档案(档案表是从表),一个档案属于一个学生。

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 ...

2.2 一对多

⼀个出版社可以出版多本书,⼀本书只能被⼀个出版社出版。将主表中的主键并到从表中做外键。

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'

2.3 多对多

⼀个买家可以购买多件商品,⼀件商品可以被多个买家购买。

# 方法一: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')

2.4 模型继承

class Parent(models.Model):
	...
	# 可以在models中添加Meta,指定是否抽象,然后进⾏继承
	class Meta:
		abstract = True/False     
		# abstract=True:将⽗类抽象化
		# 抽象化的⽗类不会再数据库⽣成表
		# ⼦类会将⽗类中的通⽤数据,复制到⼦表中

class Child(Parent):
	...

三、Form表单

Django在内部集成了一个表单模块,专门帮助我们快速处理表单相关的内容。Django的表单模块给我们提供了三个主要功能

  • 准备和重构数据用于页面渲染
  • 为数据创建HTML表单元素
  • 接收和处理用户从表单发送过来的数据

Form相关的对象包括:

  • Widget:⽤来渲染成HTML元素的部件,如:forms.Textarea对应HTML中的 < textarea > 标签
  • Field:Form对象中的⼀个字段,如:EmailField表示email字段,如果这个字段不是有效的Email地址格式,就会产⽣错误
  • Form:⼀系列Field对象的集合,负责验证和显示HTML元素
  • Form Media:⽤来渲染表单的CSS和JavaScript资源。

3.1 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,否则返回Falseform.is_valid()
has_changed()检查表单数据是否已从初始数据更改form.has_changed()
errors.as_json(escape_html=False)返回JSON序列化后的错误信息字典form.errors.as_json()

3.2 实际用法

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'))
            # <ul class="errorlist"><li>必填</li></ul>
            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官网文档

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值