• Django基础——Model篇(一)


        到目前为止,当程序涉及到数据库相关操作时,我们一般都会这么操作:
        (1)创建数据库,设计表结构和字段
        (2)使用MySQLdb来连接数据库,并编写数据访问层代码
        (3)业务逻辑层去调用数据访问层,执行数据库操作

    import MySQLdb
     
    def GetList(sql):
        db = MySQLdb.connect(user='root', db='mydb', passwd='123', host='localhost')
        cursor = db.cursor()
        cursor.execute(sql)
        data = cursor.fetchall()
        db.close()
        return data
     
    def GetSingle(sql):
        db = MySQLdb.connect(user='root', db='mydb', passwd='123', host='localhost')
        cursor = db.cursor()
        cursor.execute(sql)
        data = cursor.fetchone()
        db.close()
        return data
    python操作数据库

        Django为使用一种新的方式,即:关系对象映射(Object Relational Mapping,简称ORM)。Django遵循 Code Frist 的原则,即:根据代码中定义的类来自动生成数据库表。Django是通过Model类来操作数据库的,程序员不需要关注SQL语句和数据库的类型(无论数据库是MySql、Sqlite,还是其它类型),Django自动生成相应数据库类型的SQL语句,来完成对数据库数据的操作。

    一 创建数据库 

        当我们在已经创建的Django工程中创建app时(假设app的名称为app01),那么在app01模块下默认会生成models.py文件,这个就是Django工程中操作数据库的文件。 

     1 创建数据库Model类(models.py文件)

    #/usr/bin/env python
    #-*- coding:utf-8 -*-
    
    from __future__ import unicode_literals
    from django.db import models
    
    # Create your models here.
    
    # ORM模型
    # 类 -> 数据库表
    # 对象 -> 表中的每一行数据
    # 对象.id,对象.value -> 每行中的数据
    #这个类是用来生成数据库表的,这个类必须继承models.Model类
    
    class UserInfo(models.Model):
     #创建表的字段 
     username = models.CharField(max_length=16) #创建一个字段,类型为字符串类型,最大长度为16 
     password = models.CharField(max_length=32) #创建一个字段,类型为字符串类型,最大长度为32
    
    #注:对于ORM框架,可以简单的认为自定义类UserInfo表示数据库的表;根据UserInfo创建的对象表示数据库表
    #里的一行数据;而对象.username和对象.password表示每一行数据里具体的字段(username/password)的数据。 

     (1)更多字段类型

        一般数据库中字段类型大概5种(字符串/数字/浮点型/时间类型/布尔类型),但Django为了在后台admin中可以操作数据库,同时为了限制在admin中对数据库的无效操作,Model中设置了很多种数据类型。

    1、models.AutoField  自增列=int(11)
      如果没有的话,默认会生成一个名称为id的列,如果要显示的定义一个自增列,必须把该列设置为主键(primary_key=True)
    2、models.CharField  字符串类型字段 必须加max_length参数
    3、models.BooleanField 布尔类型字段=tinyint(1)  不能为空,Blank=True
    4、models.ComaSeparatedIntegerField  用逗号分割的数字类型=varchar 继承CharField,所以必须加max_lenght参数
    5、models.DateField  日期字段类型date
      参数auto_now=True表示每次更新都会更新这个时间;参数auto_now_add表示只是第一次创建时添加,之后的更新不再改变
    6、models.DateTimeField  日期字段类型datetime  同DateField的参数
    7、models.Decimal  十进制小数类型=decimal
      必须指定整数位max_digits和小数位decimal_places
    8、models.EmailField  字符串类型(正则表达式邮箱)=varchar  对字符串进行正则表达式验证
    9、models.FloatField  浮点类型=double
    10、models.IntegerField  整形
    11、models.BigIntegerField 长整形
      integer_field_ranges = {
        'SmallIntegerField': (-32768, 32767),
        'IntegerField': (-2147483648, 2147483647),
        'BigIntegerField': (-9223372036854775808, 9223372036854775807),
        'PositiveSmallIntegerField': (0, 32767),
        'PositiveIntegerField': (0, 2147483647),
      }
    12、models.IPAddressField  字符串类型(ip4正则表达式)
    13、models.GenericIPAddressField  字符串类型(ip4和ip6是可选的)
      参数protocol可以是:both、ipv4、ipv6  验证时,会根据设置进行报错
    14、models.NullBooleanField  允许为空的布尔类型
    15、models.PositiveIntegerFiel  正Integer
    16、models.PositiveSmallIntegerField  正smallInteger
    17、models.SlugField  减号、下划线、字母、数字
    18、models.SmallIntegerField  数字
      数据库中的字段有:tinyint、smallint、int、bigint
    19、models.TextField  字符串=longtext
    20、models.TimeField  时间 HH:MM[:ss[.uuuuuu]]
    21、models.URLField  字符串类型,地址正则表达式
    22、models.BinaryField 二进制
    23、models.ImageField   图片
    24、models.FilePathField 文件

     models.DateTimeField/models.GenericIPAddressField/models.ImageField使用字段说明

    class UserInfo(models.Model):
        name = models.CharField(max_length=32)
        ctime = models.DateTimeField(auto_now=True) #每当你创建一行数据时,Django就会在该行数据中增加一个ctime字段
        uptime = models.DateTimeField(auto_now_add=True) #当前表任何一行数据有更新时,Django就会自动更新该字段.
        #下面两项是新增的字段,注意新增时参数的设置,否则在命令行创建数据库时报错,null=True表示在数据库中该字段可以为空,default='xxx'表示默认值
         email_1 = models.EmailField(max_length=32,null=True)
        email_2 = models.EmailField(max_length=32,default='aaa@qq.com')
        #新增字段,blank=True表示admin后台可以为空
         ip = models.GenericIPAddressField(protocol='ipv4',null=True,blank=True)
    #upload_to='upload'表示用户上传数据存储的位置,这里需要注意:在数据库中实际保存的并不是文件,而是文件存放的路径 img
    = models.ImageField(null=True,blank=True,upload_to='upload')

     (2)更多参数类型

    1、null=True 数据库中字段是否可以为空
    2、blank=True django的Admin中添加数据时是否可允许空值
    3、primary_key=False 主键,对AutoField设置主键后,就会代替原来默认的自增id列
    4、auto_now和auto_now_add
      auto_now  自动创建---无论添加或修改,都是当前操作的时间
      auto_now_add  自动创建---永远是创建时的时间
    5、choices
         GENDER_CHOICE = (
              (u'M', u'Male'),
              (u'F', u'Female'),
          )
         gender = models.CharField(max_length=2,choices=GENDER_CHOICE)
    6、max_length 最大长度
    7、default  默认值
    8、verbose_name  Admin中字段的显示名称
    9、name|db_column 数据库中的字段名称
    10、unique=True  不允许重复
    11、db_index = True  数据库索引
    12、editable=True  在Admin里是否可编辑
    13、error_messages=None  错误提示
    14、auto_created=False  自动创建
    15、help_text  在Admin中提示帮助信息
    16、validators=[]  验证
    17、upload-to  文件上传路径

     (3)Model类的扩展

       输出Model对象默认返回值

    class UserInfo(models.Model):
        name = models.CharField(max_length=32)
        ctime = models.DateTimeField(auto_now=True) #每当你创建一行数据时,Django就会在该行数据中增加一个ctime字段
        uptime = models.DateTimeField(auto_now_add=True) #当前表任何一行数据有更新时,Django就会自动更新该字段.
        #下面两项是新增的字段,注意新增时参数的设置,否则在命令行创建数据库时报错,null=True表示在数据库中该字段可以为空,default='xxx'表示默认值
        email_1 = models.EmailField(max_length=32,null=True)
        email_2 = models.EmailField(max_length=32,default='aaa@qq.com')
        #新增字段,blank=True表示admin后台可以为空
        ip = models.GenericIPAddressField(protocol='ipv4',null=True,blank=True)
        #upload_to='upload'表示用户上传数据存储的位置,这里需要注意:在数据库中实际保存的并不是文件,而是文件存放的路径
        img = models.ImageField(null=True,blank=True,upload_to='upload')
    #下面的__unicode__(self)方法,表示当输出这个类创建的对象时,默认会输出这个对象的name字段
    def __unicode__(self): return self.name
    def model(request):
        #创建两条数据
        models.UserInfo.objects.create(name='alex')
        models.UserInfo.objects.create(name='eric')
        obj_1 = models.UserInfo.objects.all()
        obj_2 = models.UserInfo.objects.filter(name='alex')
        #输出obj及其类型
        print obj_1,type(obj_1)
        print obj_2,type(obj_2)
        return HttpResponse('ok')

      如果不加上述__unicode__(self)方法,则输出结果为

    [<UserInfo: UserInfo object>, <UserInfo: UserInfo object>] <class 'django.db.models.query.QuerySet'>
    [<UserInfo: UserInfo object>,] <class 'django.db.models.query.QuerySet'>

      如果加上上述__unicode__(self)方法,则输出结果为

    [<UserInfo: alex>, <UserInfo: eric>] <class 'django.db.models.query.QuerySet'>
    [<UserInfo: alex>,] <class 'django.db.models.query.QuerySet'>

      在数据库表已经生成的情况下,添加新的字段报错,错误信息如下:

    localhost:Django_lastday luotim$ python manage.py makemigrations
    You are trying to add a non-nullable field 'email' to userinfo without a default; we can't do that (the database needs something to 
    populate existing rows).
    Please select a fix: 1) Provide a one-off default now (will be set on all existing rows) 2) Quit, and let me add a default in
    models.py Select an option:

        当你在已有的数据库表中添加一个非空的字段的时,由于原表中已有的行没有这个字段,那么就会导致报错。解决办法就是:允许为空、或者设置默认值。假设在已有的表中添加如下两个字段,那么可以设置字段为空,或者给字段设置默认值

    email_1 = models.EmailField(max_length=32,null=True)
    email_2 = models.EmailField(max_length=32,default='aaa@qq.com')

      上传图片实例

    class UserInfo(models.Model):
        name = models.CharField(max_length=32)
        ctime = models.DateTimeField(auto_now=True) #每当你创建一行数据时,Django就会在该行数据中增加一个ctime字段
        uptime = models.DateTimeField(auto_now_add=True) #当前表任何一行数据有更新时,Django就会自动更新该字段.
        #下面两项是新增的字段,注意新增时参数的设置,否则在命令行创建数据库时报错,null=True表示在数据库中该字段可以为空,default='xxx'表示默认值
        email_1 = models.EmailField(max_length=32,null=True)
        email_2 = models.EmailField(max_length=32,default='aaa@qq.com')
        #新增字段,blank=True表示admin后台可以为空
        ip = models.GenericIPAddressField(protocol='ipv4',null=True,blank=True)
        #upload_to='upload'表示用户上传数据存储的位置,这里需要注意:在数据库中实际保存的并不是文件,而是文件存放的路径
        img = models.ImageField(null=True,blank=True,upload_to='upload')
        def __unicode__(self):
            return self.name
    Model类
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    
    <body>
        {#注意form表单提交文件时,form中的enctype="multipart/form-data"参数表示分片上传#}
        <form action="/upload/" method="post" enctype="multipart/form-data">
            <p><input type="file" name="f1"/></p>
            <p><input type="file" name="f2"/></p>
            <p><input type="text" name="hostname"/></p>
            <input type="submit" value="upload"/>
        </form>
    </body>
    </html>
    HTML页面
    def upload(request):
        if request.method == 'POST':
            # 这里只会获取type='text'的内容
            inp_post = request.POST
            # 这里获取提交文件的文件名,字典形式
            inp_files = request.FILES
            # 获取name='f1'对应的文件对象
            file_obj1 = inp_files.get('f1')
            print file_obj1,type(file_obj1)
            # from django.core.files.uploadedfile import InMemoryUploadedFile
            # 获取对应的文件名
            print file_obj1.name
            # 注意在windows下,打开模式为'wb',否则出错
            f = open(file_obj1.name,'wb')
            # 分片传输,每64k写一次,chunks方法中利用了yield,生成器
            print file_obj1.chunks()
            for line in file_obj1.chunks():
                f.write(line)
            f.close()
        return render(request,'home/upload.html')
    Views中的upload方法

    2 注册APP(settings.py文件)

    # Application definition
    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'app01',
    ]

     3 执行命令(Django 1.9)

    python manage.py makemigrations
    python manage.py migrate

       当执行python manage.py makemigrations命令时,在Django工程中会生成如图所示的文件(其中migrations为新生成的模块,红框中为新生成的文件)

       其中0001_initial.py文件的内容如下(相当于生成一个数据库源,后面生成数据库表会按照这个数据库源来生成)

    # -*- coding: utf-8 -*-
    # Generated by Django 1.9.2 on 2016-03-11 12:46
    
    from __future__ import unicode_literals
    from django.db import migrations, models
    
    class Migration(migrations.Migration):
        initial = True
        dependencies = []
        operations = [
            migrations.CreateModel(
                name='UserInfo',
                fields=[
                    ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                    ('username', models.CharField(max_length=16)),
                    ('password', models.CharField(max_length=32)),
                ],
            ),
        ]

       然后执行python manage.py migrate命令,它会读取0001_initial.py这个数据库结构文件从而生成数据库表。

     4 通过admin来查看数据库

       先创建超级用户,然后在admin.py文件中按照如下进行配置

    #/usr/bin/env python
    #-*- coding:utf-8 -*-
    
    from django.contrib import admin
    # Register your models here.
    from app01 import models
    
    #注册models.py文件中创建的类
    admin.site.register(models.UserInfo)

        然后可以通过页面访问到如下信息

    二 操作数据库表

     1 基本操作

    # --3种方法
    # 1种方法
    models.tb.objects.create(c1='xx', c2='oo')
    # 2种方法
    obj = models.tb(c1='xx', c2='oo')
    obj.save()
    # 3种方法--可以接受字典类型数据 **kwargs
    dic = {'c1':'xx','c2':'oo'}
    models.tb.objects.create(**dic)

    #
    models.tb.objects.get(id=123) #获取单条数据,不存在则报错(不建议)
    models.tb.objects.all() #获取全部数据
    models.tb.objects.all().first() #获取全部数据的第1条数据
    models.tb.objects.filter(name='seven') #1个参数,获取指定条件的数据
    models.tb.objects.filter(name='seven',password='123') #2个参数,获取指定条件的数据
    dic = {'name':'seven','password':'123'}
    models.tb.objects.filter(**dic) #参数可以是字典形式,获取指定条件的数据

    #
    models.tb.objects.filter(name='seven').delete() # 删除指定条件的数据
    dic = {'name':'seven','password':'123'}
    models.tb.objects.filter(**dic).delete() #参数是字典形式

    #
    # 1种方法
    models.tb.objects.filter(name='seven').update(password='123') #将指定条件的数据更新
    # 2种方法
    obj = models.tb.objects.get(id=1)
    obj.c1 = '111'
    obj.save() # 修改单条数据
    # 3种方法--字典参数
    dic = {'name':'seven','password':'123'}
    models.tb.objects.filter(**dic).update(**dic) #
    参数是字典形式

     数据库创建数据实例

    #hmtl文件
    <form action="/add_user/" method="post">
        <p>用户名:{{ obj.username }}</p>
        <p>密码:{{ obj.password }}</p>
        <input type="submit" value="submit"/>
    </form>
    
    #forms模块中的account.py文件
    class AddUser(forms.Form):
        username = forms.CharField()
        password = forms.CharField(widget=forms.PasswordInput())
    
    #views模块中的login.py文件
    def add_user(request):
        obj = AccountForm.AddUser(request.POST)
        if request.method == 'POST':
            if obj.is_valid():
                user_input = obj.clean()
                print user_input
            #用户输入的就是字典类型,所以参数为**user_input
                models.UserInfo.objects.create(**user_input)  
                print models.UserInfo.objects.all()
                return render(request,'account/add_user.html',{'obj':obj})
        return render(request,'account/add_user.html',{'obj':obj})
    
    #结果
        --print user_input
            {'username': u'xxx', 'password': u'123'}
        --print models.UserInfo.objects.all() 返回值
            [<UserInfo: xxx>, <UserInfo: alex>,]  #Model类中添加了__unicode__方法
    数据库创建数据实例

     数据库查找数据实例

    from django import forms
    from app01 import models
    
    class ImportForm(forms.Form):
        # 优化后的写法,设置select的数据为空
        admin = forms.IntegerField(
            widget=forms.Select()
        )
        # 为了实时的获取文件中的数据,重新写了构造方法,这样当修改了文件数据,重新请求时(不需要重启)
        # 实时的获取文件中的数据,因为每次请求时都会把类实例化,那么就会执行类的构造方法,这里利用了
        # 面向对象编程的思想(静态字段属于类),好好体会这里面的思想
        def __init__(self,*args,**kwargs):
            super(ImportForm,self).__init__(*args,**kwargs)
            # 1、从文件中获取数据,然后赋值给self.fields['admin'].widget.choices
            # import json
            # f = open('db_admin')
            # data = f.read()
            # data_tuple = json.loads(data)
            # self.fields是深拷贝的结果self.fields = copy.deepcopy(self.base_fields)
            # self.fields['admin'].widget.choices = data_tuple
    
            # 2、直接从数据库中获取self.fields['admin'].widget.choices
            self.fields['admin'].widget.choices = models.SimpleModel.objects.all().values_list('id','username')
    forms模块中的ImportForm类
    from django.shortcuts import render,HttpResponse
    from app01.forms import home as HomeForm
    from app01 import models
    
    def index(request):
        # 创建一行数据
        # dic = {'username':'alex','password':'123'}
        # models.SimpleModel.objects.create(**dic)
    
        res = models.SimpleModel.objects.all()
        #对应的sql语句"SELECT "app01_simplemodel"."id", "app01_simplemodel"."username", "app01_simplemodel"."password" FROM "app01_simplemodel""
        print res.query
        #[<SimpleModel: SimpleModel object>] 列表
        print res
        # <class 'django.db.models.query.QuerySet'> 类
        print type(res)
        # 取某个字段值,字典
        ret = models.SimpleModel.objects.all().values('username')
        # [{'username': u'alex'}] 字典
        print ret
        # <class 'django.db.models.query.QuerySet'>
        print type(ret)
        # 取某个字段值,列表
        ref1 = models.SimpleModel.objects.all().values_list('username')
        # [(u'alex',)] 元组组成的列表
        print ref1
        # <class 'django.db.models.query.QuerySet'>
        print type(ref1)
        # [(1, u'alex')] 获取(id,username)元组组成的列表
        ref2 = models.SimpleModel.objects.all().values_list('id','username')
        print ref2
    
        obj = HomeForm.ImportForm()
        return render(request,'home/index.html',{'obj':obj})
    Views模块中的index函数
    class SimpleModel(models.Model):
        username = models.CharField(max_length=64)
        password = models.CharField(max_length=64)
    models模块中的SimpleModel类
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
        <h1>录入数据</h1>
        <form action="/index/">
           <p>{{ obj.host_type }} </p>
           <p>{{ obj.hostname }}</p>
           <p>{{ obj.admin }}</p>
        </form>
        <h1>数据列表</h1>
    </body>
    </html>
    index.html文件

    值得注意的地方:

    #获取指定列的值,可以传多个参数,它的结果为字典形式
    models.UserInfo.objects.all().values('password')
    #结果  
    [{'password': u'aaa'}, {'password': u'bbb'}, {'password': u'ccc'}]

    #获取指定列的值,可以传多个参数,它的结果为指定列的值先组成元组,然后再由这些元组组成列表
    models.UserInfo.objects.all().values_list('password')
    #结果
    [(u'alex',), (u'eric',), (u'rain',),]

    #在form中生成select标签时,可以通过它来获取值然后给choices
    models.UserInfo.objects.all().values_list('id','username')
    #结果
    [(1, u'alex'), (2, u'eric'), (3, u'rain'),]

     2 进阶操作(了不起的双下划线)

       利用双下划线将字段和对应的操作连接起来

    # 获取个数
    models.Tb1.objects.filter(name='seven').count()

    # 大于,小于
    models.Tb1.objects.filter(id__gt=1) # 获取id大于1的值
    models.Tb1.objects.filter(id__lt=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等于112233的数据
    models.Tb1.objects.exclude(id__in=[11, 22, 33]) # not in

    # contains(类似于数据库中like的语法)
    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

    #以什么开始,以什么结束,和上面一样带i的是大小写不敏感
    # startswithistartswith, endswith, iendswith

    #排序
    models.Tb1.objects.filter(name='seven').order_by('id') # asc正序
    models.Tb1.objects.filter(name='seven').order_by('-id') # desc反序

    #分页时使用
    models.Tb1.objects.all()[10:20] #取所有数据的10条到20条,分页的时候用的到

    #分组使用
    from django.db.models import Count, Min, Max, Sum
    models.Tb1.objects.all().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"

    参考资料:

         http://www.cnblogs.com/wupeiqi/articles/5237704.html

         http://www.cnblogs.com/wupeiqi/articles/5246483.html

  • 相关阅读:
    像Google Play一样让DrawerLayout拉出的抽屉在透明系统状态栏和工具栏(ToolBar)之间。
    WebView中实现延迟加载,图片点击时才加载。
    MediaPlayer配合SurfaceView或TextureView做视频播放器时的截图方法。
    解决Fragment中使用ViewPager时,ViewPager里的Fragment错位和空白问题。
    使用SAE的Storage来为Android应用提供版本更新的检查和下载功能
    使用SAE的服务来实现android端的用户反馈功能。
    使用IntentService给自己的Android应用写一个文件下载器。
    禁止进入Activity时NumberPicker自动弹出输入法。
    使用Android系统提供的DownloadManager来下载文件。
    Android异步下载图片的类和缓存图片到SD卡的类。
  • 原文地址:https://www.cnblogs.com/maociping/p/5255834.html
Copyright © 2020-2023  润新知