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

时间:2023-09-25 15:48:44
一、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=)
        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  object(s) from  fixture(s)

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

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

root@w:~/learn_models# python manage.py shell
Python 2.7. (default, Oct  , ::) 
[GCC 5.2. ] on linux2
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from people.models import Person
>>> Person.objects.create(name="wulaoer", age=)
<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=)
        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、减少文本的长度,保存数据的时候压缩,读取的时候解压缩,如果发现压缩后更长,就用原文本直接存储:

 class CompressedTextField(models.TextField):
""" model Fields for storing text in a compressed format (bz2 by default) """
__metaclass__ = models.SubfieldBase def to_python(self, value):
if not value:
return value try:
return value.decode('base64').decode('bz2').decode('utf-8')
except Exception:
return value def get_prep_value(self, value):
if not value:
return value try:
value.decode('base64')
return value
except Exception:
try:
tmp = value.encode('utf-8').encode('bz2').encode('base64')
except Exception:
return value
else:
if len(tmp) > len(value):
return value return tmp

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

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

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

 from django.db import models
import ast class ListField(models.TextField):
__metaclass__ = models.SubfieldBase
description = "Stores a python list" def __init__(self, *args, **kwargs):
super(ListField, self).__init__(*args, **kwargs) def to_python(self, value):
if not value:
value = [] if isinstance(value, list):
return value return ast.literal_eval(value) def get_prep_value(self, value):
if value is None:
return value return unicode(value) # use str(value) in Python def value_to_string(self, obj):
value = self._get_val_from_obj(obj)
return self.get_db_prep_value(value)

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

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

在终端上尝试:

 >>> from app.models import Article
>>> d = Article()
>>> d.labels
[]
>>> d.labels = ["Python", "Django"]
>>> d.labels
["Python", "Django"]

示例代码

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

 >>> from blog.models import Article

 >>> a = Article()
>>> a.labels.append('Django')
>>> a.labels.append('custom fields') >>> a.labels
['Django', 'custom fields'] >>> type(a.labels)
<type 'list'> >>> a.content = u'我正在写一篇关于自定义Django Fields的教程'
>>> 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/