Django之路:模型(数据库)和自定义Field以及数据表的更改

it2022-05-20  98

一、Django 模型(数据库)

 Django模型是与数据库相关的,与数据库相关的代码一般写在models.py中,Django支持sqlite3,MySQL,PostgreSQL等数据库,只需要在settings.py中配置即可,不用更改models.py中的代码,丰富的API极大的方便了使用。

本节的代码:(Django 1.6,Python 2.7 测试环境)

可以按照我的步骤来开始做:

root@w:~# django-admin startproject learn_models #新建一个项目 root@w:~# cd learn_models/  #进入该项目的文件夹 root@w:~/learn_models# django-admin startapp people #新建一个people应用(app)

 补充:新建app也可以用python manage.py startapp people,需要指出的是,django-admin.py是安装Django后多出的一个命令,并不是指一个django-admin.py脚本在当前目录下。

那么project和app什么关系呢,一个项目一般包含多个应用,一个应用也可以用在多个项目中。

将我们新建的应用(people)添加到settings.py中的INSTALLED_APPS中,也就是告诉Django有这么一个应用。

root@w:~# sudo vi learn_models/learn_models/settings.py  ...................省略部分........................ INSTALLED_APPS = (      ' django.contrib.admin ',      ' django.contrib.auth ',      ' django.contrib.contenttypes ',      ' django.contrib.sessions ',      ' django.contrib.messages ',      ' django.contrib.staticfiles ',      ' people ', ) ...................省略部分........................

 我们打开people/models.py文件,修改其中的代码如下:

root@w:~# sudo vi learn_models/people/models.py  from django.db import models class Person(models.Model):         name = models.CharField(max_length= 30)         age = models.IntegerField()

 新建了一个Person类,继承自models.Model,一个人有姓名和年龄。这里用到了两种Field,更多猛插这里

 Fields相关官方文档:https://docs.djangoproject.com/en/dev/ref/models/fields/

 同步以下数据库

python manage.py syncdb # 进入 manage.py 所在的那个文件夹下输入这个命令   注意:Django  1.7 及以上的版本需要用以下命令 python manage.py makemigrations python manage.py migrate root@w:~# cd learn_models/ root@w:~/learn_models# python manage.py syncdb Creating tables ... Creating table django_admin_log Creating table auth_permission Creating table auth_group_permissions Creating table auth_group Creating table auth_user_groups Creating table auth_user_user_permissions Creating table auth_user Creating table django_content_type Creating table django_session Creating table people_person You just installed Django ' s auth system, which means you don 't have any superusers defined. Would you like to create one now? (yes/no): yes Username (leave blank to use  ' root '): root#数据库用户名 Email address:  Password: #数据库密码 Password (again):  Superuser created successfully. Installing custom SQL ... Installing indexes ... Installed  0  object(s)  from  0 fixture(s)

 到这会看到,Django生成了一系列的表,也生成了新建的people_persona这个表,想知道如何使用这个表,请继续往下看。

Django提供了丰富的API,下面演示如何使用它。

root@w:~/learn_models# python manage.py shell Python  2.7. 10 ( default, Oct  14  201516: 09: 02)  [GCC  5.2. 1  20151010] on linux2 Type  " help "" copyright "" credits " or  " license "  for more information. (InteractiveConsole) >>>  from people.models import Person >>> Person.objects.create(name= " wulaoer ", age= 20) <Person: Person  object>

 新建了一个用户wulaoer,那么如果从数据库中查询到它呢?

>>> Person.objects. get(name= " wulaoer ") <Person: Person  object>

 这里用到了.objects.get()方法查询出来符合条件的对象,但是上面的查询结果显示<Person: Person object>,这里并没有显示出与wulaoer相关的信息,如果用户多了就无法知道查询出来的到底是谁,查询结果是否正,我们重新修改以下

name和age等字段中不能有__(双下划线,因为在Django QuerySet API中有特殊含义(用于关系,包含,不区分大小写,以什么开头或结尾,日期的大于小于,正则等))

也不能有Python中的关键字,name 是合法的,student_name也合法,但是student__不合法,try,class,continue也不合法,因为它是Python的关键字(import keyword; print(keyword.kwlist)可以打出所有的关键字)

root@w:~# sudo vi learn_models/people/models.py from django.db import models class Person(models.Model):         name = models.CharField(max_length= 30)         age = models.IntegerField()         def __unicode__(self):         #在Python3中使用 def __str__(self)                  return self.name

 按CTRL + C或exit()退出的Python shell,重复上面的操作,我们就可以看到:

>>>  from people.models import Person >>> Person.objects. get(name= " wulaoer ") <Person: wulaoer>

 新建一个对象的方法有以下几种:

1、Person.objects.create(name=name,age=age) 2、p = Person(name="wulaoer", age=23)      p.save() 3、p = Person(name="laowu")      p.age = 23      p.save() 4、Person.objects.get_or_create(name="laowu", age=23)

 这种方法是防止重复很好的方法,但是速度要相对慢些,返回一个元组,第一个为Person对象,第二个为True或False,新建时返回的是True,已经存在时返回False。

获取对象有以下方法:

1、Person.objects.all() 2、Person.objects.all()[:10] 切片操作,获取10个人,不支持负索引,切片可以节约内存 3、Person.objects.get(name=name) get是用来获取一个对象的,如果需要获取满足条件的一些人,就要用到filter 4、Person.objects.filter(name="abc") # 等于Person.objects.filter(name__exact="abc") 名称严格等于 "abc" 的人 5、Person.objects.filter(name__iexact="abc") # 名称为 abc 但是不区分大小写,可以找到 ABC, Abc, aBC,这些都符合条件 6、Person.objects.filter(name__contains="abc") # 名称中包含 "abc"的人 7、Person.objects.filter(name__icontains="abc") #名称中包含 "abc",且abc不区分大小写 8、Person.objects.filter(name__regex="^abc") # 正则表达式查询 9、Person.objects.filter(name__iregex="^abc")# 正则表达式不区分大小写 filter是找出满足条件的,当然也有排除符合某条件的 10、Person.objects.exclude(name__contains="LW") # 排除包含 LW 的Person对象 11、Person.objects.filter(name__contains="abc").exclude(age=23) # 找出名称含有abc, 但是排除年龄是23岁的 二、Django 自定义(Field)

 Django 的官方提供了很多的Field,但是有时候还是不能满足我们的需求,不过Django提供了自定义Field的方法:

提示:如果现在用不到可以跳过这一节,不影响后面的学习,等用到的时候在学习也可以。

这里举一个简单的例子。

1、减少文本的长度,保存数据的时候压缩,读取的时候解压缩,如果发现压缩后更长,就用原文本直接存储:

1 class CompressedTextField(models.TextField): 2 """ model Fields for storing text in a compressed format (bz2 by default) """ 3 __metaclass__ = models.SubfieldBase 4 5 def to_python(self, value): 6 if not value: 7 return value 8 9 try: 10 return value.decode('base64').decode('bz2').decode('utf-8') 11 except Exception: 12 return value 13 14 def get_prep_value(self, value): 15 if not value: 16 return value 17 18 try: 19 value.decode('base64') 20 return value 21 except Exception: 22 try: 23 tmp = value.encode('utf-8').encode('bz2').encode('base64') 24 except Exception: 25 return value 26 else: 27 if len(tmp) > len(value): 28 return value 29 30 return tmp

to_python函数用于转化数据库中的字符到python的变量,get_prep_value用于将python变量处理后(此处为压缩)保存到数据库,使用和Django自带的Field一样。

2、比如想保存一个列表到数据库中,在读取用的时候要python的列表的形式,我们来自己写一个ListField:

这个ListField继承自TextField,代码如下:

1 from django.db import models 2 import ast 3 4 class ListField(models.TextField): 5 __metaclass__ = models.SubfieldBase 6 description = "Stores a python list" 7 8 def __init__(self, *args, **kwargs): 9 super(ListField, self).__init__(*args, **kwargs) 10 11 def to_python(self, value): 12 if not value: 13 value = [] 14 15 if isinstance(value, list): 16 return value 17 18 return ast.literal_eval(value) 19 20 def get_prep_value(self, value): 21 if value is None: 22 return value 23 24 return unicode(value) # use str(value) in Python 3 25 26 def value_to_string(self, obj): 27 value = self._get_val_from_obj(obj) 28 return self.get_db_prep_value(value)

这个使用很简单,首先导入ListField,像自带的Field一样使用:

1 class Article(models.Model): 2 labels = ListField()

在终端上尝试:

1 >>> from app.models import Article 2 >>> d = Article() 3 >>> d.labels 4 [] 5 >>> d.labels = ["Python", "Django"] 6 >>> d.labels 7 ["Python", "Django"]

示例代码

下载上面的代码,解压,进入项目目录,输入python manage.py shell搞起

1 >>> from blog.models import Article 2 3 >>> a = Article() 4 >>> a.labels.append('Django') 5 >>> a.labels.append('custom fields') 6 7 >>> a.labels 8 ['Django', 'custom fields'] 9 10 >>> type(a.labels) 11 <type 'list'> 12 13 >>> a.content = u'我正在写一篇关于自定义Django Fields的教程' 14 >>> a.save() 三、Django 数据表更改

   在工作中我们设计数据库的时候,早期设计完后,后期发现不完善,要对数据表进行更改,这时候就要用到下面的知识了。

Django 1.7.x 和后来的版本:

python manage.py makemigrations python manage.py migrate

这两个命令就是对models.py进行检测,自动发现需要更改的,应用到数据库中。

Django 1.6.x 及以前:

在Django 1.6以及以前的版本中,我们测试,当发现model要更改,怎么办?

修改了models.py之后,我们运行:

python manage.py syncdb

这个命令只会将我们在models.py中新加的类创建相应的表。

对于原来有的,现在删除了类,Django会询问是否要删除数据库中已经存在的相关数据表。

如果在原来的类上增加字段或者删除字段,可以参考这个命令:

python manage.py sql appname

给出的SQL语句,然后自己手动到数据库执行SQL。但是这样容易出错!

Django的第三方 app South就是专门做数据库表结构自动迁移工作,Jacob Kaplan-Moss曾做过一次调查,South名列最受欢迎的第三方app。事实上,它现在已经俨然成为Django事实上的数据库表迁移标准,很多第三方app都会带South migration脚本,Django 1.7中集成了South的功能。

1、安装South

(sudo) pip install South

2、使用方法

South的宗旨是简单,只需要几步。针对已经建好model和创建完表的应用。

把south加入到settings.py中的INSTALL_APPS中

# Application definition INSTALLED_APPS = ( 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'blog', 'south', )

修改好之后运行一次python manage.py syncdb,Django会新建一个south_migrationhistory表,用来记录数据表更改(Migration)的历史记录。

$ python manage.py syncdb Syncing... Creating tables ... Creating table south_migrationhistory Installing custom SQL ... Installing indexes ... No fixtures found. Synced: > django.contrib.admin > django.contrib.auth > django.contrib.contenttypes > django.contrib.sessions > django.contrib.messages > django.contrib.staticfiles > blog > south Not synced (use migrations):

把之前建好的blog这个app使用South来管理:

$ python manage.py convert_to_south blog

这时,你会发现blog文件夹中多了一个migration目录,里面有一个0001_initial.py文件。

注:如果blog这个app之前就创建过相关的表,可以用下面的来“假装”用South创建(伪创建,在改动models.py之前运行这个)

python manage.py migrate blog --fake

意思是这个表我以前已经创建好了,用South只是记一下这个创建记录,下次migrate的时候不必在创建了。

原理就是south_migrationhistory中记录下了models.py的修改的历史,下次在修改时会和最近一次记录比较,发现改变了什么,然后生成相应的对应文件,最终执行相应的SQL更改原有的数据表。

接着,当你对Blog.models做任何修改后,只要执行:

python manage.py schemamigration blog --auto

South就会帮助我们找出哪些地方做了修改,如果你新增的数据表没有给default值,并且没有设置null=True,south会问你一些问题,因为新增的column对于原来的旧的数据不能为Null的话就得有一个值。顺利的话,在migrations文件夹下会产生一个002_add_mobile_column.py,但是这一步并没有真正修改数据库的表,我们需要执行python manage.py migrate:

$ python manage.py migrate Running migrations for blog: - Migrating forwards to 0002_add_mobile_column. > blog:0002_add_mobile_column - Loading initial data for blog. No fixtures found.

这样所做的更改就写入了数据库中。

恢复到以前

South好处就是可以随时恢复到之前的一个版本,比如我们想要回到最开始的哪个版本:

> python manage.py migrate blog 0001 - Soft matched migration 0001 to 0001_initial. Running migrations for blog: - Migrating backwards to just after 0001_initial. < blog:0002_add_mobile_column

这样就搞定了,数据库就恢复到以前了,比你手动更改要方便多了。  

 

参考网址:

https://djangosnippets.org/snippets/2014/https://docs.djangoproject.com/en/dev/howto/custom-model-fields/

 来源参考:http://www.ziqiangxuetang.com/

转载于:https://www.cnblogs.com/wulaoer/p/5102425.html


最新回复(0)