[Django系列]8.表关联操作,自定义field,sql

1.表关联操作

1.1在models.py下映射出3张表到数据库
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
class Publisher(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)
​
    def __unicode__(self):
        return self.name
class Author(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()
    email = models.EmailField()
​
    def __unicode__(self):
        return self.name
class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.ManyToManyField(Author)
    publisher = models.ForeignKey(Publisher)
    def __unicode__(self):
        return self.title
  • 三张表数据
#book_book_authors
id  name age email
1   浮生  29  toc@163.com
2   杨帆  25  yangfan@qq.com
3   侠仔  25  xiazai@qq.com
4   slice   26  slice@tanzhou.com
5   ff  26  testsat@qq.com
6   yf  23  yf@test.com
#book_book
id  title  publihser_id
1   论一夫多妻   2
2   从删库到跑路  1
3   FUCK    2
4   more and more   2
5   django  4
#book_publisher
id name address
1   人民教育出版社 北京市解放西路
2   不可描述出版社 火星非主流市
3   ffd ffffffffd
1.2现在需要往数据库添加一条book记录,shell模式下操作
>>>python mange.py shell  #进入python shell 模式
>>>from book.models import Author,Book,Publisher #导入Author,Book,Publisher模块>>> a=Author(name='laocao',age=19)  #增加一条作者laocao的记录
>>> a.save()                        #作者入库
>>> b=Book(title='mysql run',publisher_id=1)   #增加一条书籍记录
>>> b.save()                                    #入库
>>> b.authors.add(a)                           #将刚才新增的作者laocao关联进来
>>> b.save()
  • 需要注意的是,使用此方法进行插入数据,需要用a.save() 事先对外键的记录进行入库.

  • 书籍增加的时候,也需要b.save()先入库,才能操作多对多关联b.authors.add(a) 操作,否则会报错.

1.3多对多查询
>>> Author.objects.filter(name='laocao')[0].book_set.all()
<QuerySet [<Book: mysql run>]>   #Queryset对象,访问需要[0]
----------------------------------------------------------------------
>>> Author.objects.filter(name='laocao')[0].book_set.all()[0]
<Book: mysql run>
>>> Author.objects.filter(name='laocao')[0].book_set.all().first()
<Book: mysql run>
>>> Author.objects.filter(name='laocao')[0].book_set.all().last()
<Book: mysql run>
1.4外键的级联删除
  • 首先需要修改models.py文件,将上面的authors字段从多对多models.ManyToManyField(Author)修改为一对一authors = models.OneToOneField(Author,null=True,on_delete=models.CASCADE)
  • on_delete=models.CASCADE该字段参数表名使用级联删除
  • Do not Foget to python manage.py makemigrations & python manage.py migrate
#models.py
class Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.OneToOneField(Author,null=True,on_delete=models.CASCADE)
    publisher = models.ForeignKey(Publisher)
    def __unicode__(self):
        return self.title
首先增加一个作者Fusheng (shell模式下操作)
>>> a=Author(name='Fusheng',age=29)
>>> a.save()

可以看到新增的作者Fusheng的id=9,新增一本书Goudan
>>> b=Book(title='Goudan',publisher_id=1)
>>> b.save()
>>> b.authors_id=9
>>> b.save()

现在删除作者Fusheng的记录
>>> Author.objects.get(name='Fusheng').delete()
>>>(2, {u'book.Book': 1, u'book.Author': 1}) #操作记录数2条
  • 可以看到,因为authors字段存在级联删除,所以当作者Fusheng被删除了,书籍Goudan也被级联删除

2.自定义field

Django常用的Field已经足够日常使用,如果内置Field无法满足需求,可以自定义Filed
实质是通过对内置的Filed类进行继承和派生
2.1自定义一个类ListField,该类是从TextField派生,定义了3个方法,实现的作用是
to_python 函数用于转化数据库中的字符到 Python的变量
get_prep_value 用于将Python变量处理后保存到数据库,
value_to_string 用于print
#book/ListField.py
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 3
    def value_to_string(self, obj):
        value = self._get_val_from_obj(obj)
        return self.get_db_prep_value(value)
2.2改动models.py,首先需要导入from book.ListField import ListField在Author类下增加字段area,使用自定义的类ListField
  • 别忘了 python manage.py makemigrations & python manage.py migrate
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
from book.ListField import ListFieldclass Publisher(models.Model):
    name = models.CharField(max_length=30)
    address = models.CharField(max_length=50)
    def __unicode__(self):
        return self.nameclass Author(models.Model):
    name = models.CharField(max_length=30)
    age = models.IntegerField()
    email = models.EmailField()
    area = ListField(null=True)    #增加area字段,引用自定义Field
    def __unicode__(self):
        return self.nameclass Book(models.Model):
    title = models.CharField(max_length=100)
    authors = models.OneToOneField(Author,null=True,on_delete=models.CASCADE)
    publisher = models.ForeignKey(Publisher)
​
    def __unicode__(self):
        return self.title
2.3 在shell模式下增加一个作者xiazai的记录,使用自定义Field
F:\Project\git\web\day10>python manage.py shell
Python 2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:42:59) [MSC v.1500 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from book.models import Author
>>> a=Author(name='xiazai',age=25,email='xiazai@toc.com')
>>> a.area=['beijing','xian','guangxi']  #该字段定义所在的区域,是一个列表
>>> a.save()
  • 数据入库成功,自动增加了area字段

3.自定义sql

3.1 Manager.raw() :执行原始查询并返回模型实例,格式是Author.objects.raw(sql),返回的是一个RawQuerySet对象.
shell模式下
>>> Author.objects.raw('select * from book_author')
<RawQuerySet: select * from book_author>          #返回的是RawQuerySet对象
>>> b=Author.objects.raw('select * from book_author')   
>>> b[0].name                                     #可以直接使用[0]选中记录
u'\u6d6e\u751f'
>>> print b[0].name 
浮生                                                                                                         
>>> for i in b:                                    #可以被迭代输出
...     print i.name
浮生
杨帆
侠仔
slice
ff
yf
laocao
xiazai
3.2 Executing custom SQL directly直接执行自定义SQL,这种方式可以完全避免数据模型,直接执行原始的SQL语句
django.db.connection:代表默认的数据库连接 
django.db.transaction :代表默认数据库事务(transaction 
database connection调用 connection.cursor() 得到一个游标(cursor)对象。 
然后调用 cursor.execute(sql, [params]) 执行SQL 
cursor.fetchone() 或者 cursor.fetchall() 返回结果行
如果执行修改操作,则调用 transaction.commit_unless_managed()来保证你的更改提交到数据库。
该方式直接连接数据库执行sql操作,不经过数据模型.
>>> from django.db import connection,transaction #导入连接模块,传输模块
>>> cursor=connection.cursor()                   #获取游标
>>> cursor.execute('select * from book_author')  #执行sql语句
<django.db.backends.sqlite3.base.SQLiteCursorWrapper object at 0x024EBA28>  #返回一个SQLiteCursorWrapper对象
>>> row=cursor.fetchall()                       #用fectchall()获取结果集合
>>> row                                         #显示结果集
[(1, u'\u6d6e\u751f', 29, u'toc@163.com', None), (2, u'\u6768\u5e06', 25, u'yangfan@qq.com', None), (3, u'\u4fa0\u4ed4', 25, u'xiazai@qq.com', None), (4, u'slice', 26, u'slic
e@tanzhou.com', None), (5, u'ff', 26, u'testsat@qq.com', None), (6, u'yf', 23, u'yf@test.com', None), (8, u'laocao', 19, u'', None), (9, u'xiazai', 25, u'xiazai@toc.com', u"[
'beijing', 'xian', 'guangxi']")]
>>> row[0]                                       #显示第一条记录
(1, u'\u6d6e\u751f', 29, u'toc@163.com', None)
当然也可以传条件参数
>>> cursor.execute('select * from book_author where name=%s',['yf'])
<django.db.backends.sqlite3.base.SQLiteCursorWrapper object at 0x024EBA28>
>>> row=cursor.fetchone()
>>> row
(6, u'yf', 23, u'yf@test.com', None)

总结

本次内容涉及数据库核心操作,请务必学会!
赞(0) 打赏
未经允许不得转载:http://www.yueguangzu.net逗逗网 » [Django系列]8.表关联操作,自定义field,sql
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

微信扫一扫打赏