Django学习记录

Django学习记录,第1张

Django学习记录

注:此博客为仅为个人学习记录,不定期更新

Django应用及分布式路由 创建新的应用
  1. 创建方法:终端输入python manage.py startapp news,其中news是新的应用名

  2. 注册创建的APP,在项目的配置文件中(settings.py)找到INSTALLED_APPS列表,并在该列表中添加项目名,如下:

    INSTALLED_APPS = [
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'music',
        'sport',
        'news',
    ]
    

    最后的news即为新添加的应用

分布式路由

创建分布式路由的目的是为了让单个应用有单个应用的专属路由,不用一直在项目主路由中添加地址,正常的逻辑是创建了新的应用后,应该在项目主路由中为该应用配置专属的路由,而这个新建应用的路由则在自己的路由文件中去添加

  1. 在项目路由中添加新的应用路由地址,如下:

    urlpatterns = [
        path('admin/', admin.site.urls),
        path('img', views.test_static),
        path('music/', include('music.urls')),
        path('sport/', include('sport.urls')),
        path('news/', include('news.urls')),
    ]
    

    列表的最后即为news应用的专属路由,而使用include方法,表明以后news的路由应该到news自己的urls.py文件中去添加

  2. 在新建应用下,创建一个urls.py来添加本应用的路由地址和对应的视图函数,如下:

    from django.urls import path
    from news import views
    
    urlpatterns = [
        path('index', views.index_view),
    ]
    

    index是news应用的地址,这种情况下,访问域名127.0.0.1:8000/news/index,浏览器返回的就是视图函数views中index_view方法所写的内容

模型层及ORM介绍

模型:

是Python的一个类,要求必须继承自django.db.models.Model, 一个模型类代表数据库中的一张数据表,模型类的每个熟悉代表数据库的一个字段,模型是数据增删改查的接口,用来 *** 作数据库

ORM框架:

对象关系映射,允许使用类和对象对数据库进行 *** 作,避免通过SQL语句 *** 作数据库

终端进入MySQL

可以从cmd终端进入,也可以从编译器终端进入,指令都一样

mysql -uroot -p

接下来在终端输入密码

退出在终端输入quit即可

数据库模型文件

通过models.py生成数据表,如下

from django.db import models

# Create your models here.


class Book(models.Model):
    title = models.CharField("书名", max_length=50, default='')
    price = models.DecimalField("定价", max_digits=7, decimal_places=2, default=0.0)

通过上面这个文件,执行后应该会在数据库中生成一张Book数据表,这张表有两个字段,分别是title和price,但仅写完这个文件是不够的,需要在终端执行如下命名:

  1. 生成迁移文件,终端执行python manage.py makemigrations,将应用下的models.py文件生成一个中间件,并保存在migration文件夹中
  2. 执行迁移脚本程序,终端执行python manage.py migrate,将每个应用下的migration目录中的中间文件同步回数据库
ORM基础字段及选项 字段类型
  • BooleanField()布尔型
  • CharField()字符型
  • DateField()日期,参数:
    • auto_now:每次更新对象时,自动设置该字段为当前时间(取值True/False)
    • auto_now_add:对象第一次创建时自动设置当前时间(取值True/False)
    • default:设置当前时间
  • DateTimeField()日期时间,高频使用,比上面那个更频繁使用,基本创建表就会使用,一般会建两个字段,创建时间和修改时间,参数与DateField()相同
  • FloatField()浮点型
  • DecimalField()小数
  • EmailField(),数据库类型:varchar,Django封装好的
  • IntegerField()
  • ImageField()图片的存储路径,字符串
  • TextField() 类型longtext
  • ……
字段选项
  • 主键primary_key
  • blank,设置为True时,可以为空,设置为false时,字段必须填写
  • null,设置为True表示允许为空,默认为False
  • default,设置所在列的默认值,如果null=False建议添加此项
  • db_index,索引
  • unique, 唯一索引
  • db_column,指定字段名,一般不用给
  • verbose_name,admin上的显示名称
模型类Meta

使用内部类给模型赋予属性,比如修改表名

class Meta:
	db_table = 'book'
ORM基本 *** 作-创建数据

models.Model模型类中的objects对象被继承,增删改查都是通过这个objects对象来实现

终端进入Django shell的命令:

python manage.py shell

第一种方法:

MyModel.models.Model.objects.create()

第二种方法:

创建模型对象实例,调用save()保存

obj = MyModel(attr1=value1, attr2=value2....)
obj.attr = value
obj.save()
ORM查询
  • objects.all() == select * from tabel,返回QuerySet容器对象,内部存放模型实例

    一般情况下,为了更好的显示查询结果,都要重新____str____函数,这是为了设置模型的返回值

    以下是查询示例

    from bookstore.models import Book
     al = Book.models.all()
    

    重写str函数

        def __str__(self):
            return '%s_%s_%s_%s' % (self.title, self.pub, self.price, self.market_price)
    

    查询结果

    , , , , ]>
    
  • objects.values(‘列1’, ‘列2’),查询部分列数据,返回QuerySet字典列表,等同于

    select lie1, lie2
    from Book
    
  • objects.values_list(‘列1’, ‘列2’),查询部分列数据,返回QuerySet元组列表

  • order_by()查询结果排序

ORM综合练习

目的:将数据库中,所有的book信息显示在all_book页面上,最终结果如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fD7K0o77-1651225916928)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220429095725708.png)]

  1. 创建分布式路由:在主项目的urls.py文件中添加bookstore应用的路由,如下

    from django.contrib import admin
    from django.urls import path, include
    
    from mysite import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('img', views.test_static),
        path('music/', include('music.urls')),
        path('sport/', include('sport.urls')),
        path('news/', include('news.urls')),
        path('bookstore/', include('bookstore.urls')),
    ]
    

    最后一行即为bookstore应用的分布式路由

  2. 在bookstore应用中创建该应用的路由,在应用目录下新建一个urls.py,填入一下路由信息

    from django.urls import path
    
    from bookstore import views
    
    urlpatterns = [
        path('all_book', views.all_book),
    ]
    

    也就是当我访问http://127.0.0.1:8000/bookstore/all_book时指向的文件和视图函数

  3. 视图函数,在views.py文件中添加查询和渲染网页的代码

    from django.shortcuts import render
    from .models import Book
    
    
    # Create your views here.
    
    
    def all_book(request):
        all_book = Book.objects.all()
        return render(request, 'bookstore/all_book.html', locals())
    
    

    使用all()方法查询到所有的数据,并把查到的all_book传递给模板文件夹(templates)下bookstore文件夹中的all_book.html文件,locals()函数将查询到的局部变量转换成字典数据,也就是把all_book转换成字典,讲真,我真没理解这个locals到底怎么用的

  4. 模板文件,在模板中填写网页展示的内容,并接受视图函数传递过来的数据,并进行展示

    DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>查看所有表格title>
        <style>
            td, th {
                border: 1px solid;
    
            }
            .mt {border-collapse: collapse;}
        style>
    head>
    <body>
    <table class="mt">
        <tr>
            <th>idth>
            <th>titleth>
            <th>pubth>
            <th>priceth>
            <th>market_priceth>
            <th>opth>
        tr>
        {% for book in all_book %}
            <tr>
                <td>{{ book.id }}td>
                <td>{{ book.title }}td>
                <td>{{ book.pub }}td>
                <td>{{ book.price }}td>
                <td>{{ book.market_price }}td>
                <td>
                    <a href="#" >更新a>
                    <a href="#">删除a>
                td>
            tr>
    
        {% endfor %}
        <tr>tr>
    table>
    body>
    html>
    

    这里关键是要理解那一段for循环的写法

    不写CSS样式显示出来的表格很丑,没有边框,我在样式中改了一下边框,在两个地方

小结1

其实写到这里,Django的作用和整个 *** 作流程,已经大致显示出来了,有必要做个小结:

  1. 首先启动一个Django项目——mysite
  2. 在Django项目中新建一个应用——bookstore
  3. 为项目(mysite)创建分布式路由,在路由中添加bookstore的分布式路由
  4. 在应用(bookstore)中添加路由,指定地址指向的视图函数
  5. 在应用的视图函数中添加方法,如果需要向数据库传递数据,就要启动数据库模型
    1. 在应用的model.py文件中添加模型,注意,一个模型就是一张表,可以在这个表中进行增删改查 *** 作
    2. 如果有必要,重写一下默认的str方法
    3. 视图函数中对模型数据进行处理,可以查询、排序等 *** 作,并返回查询的值
  6. 在应用中创建模板文件(html文件),需要在应用下新建一个templates文件夹,最好是在templates文件夹下再创建一个与应用同名的文件夹(防止路由找不到模板文件)用来存放模板文件,在模板文件下编写页面文件,并接收视图函数传递过来的数据,使用Django特有的语法处理接收到的数据并写入到模板文件中
  7. 启动项目,访问地址,即可展示页面
条件查询 普通查询
  • filter()方法,返回QuerySet

    MyModel.objects.filter(attr1=value1, attr2=valur2…)

    多个属性在一起时为“与”关系

  • exclude(条件),不包含此条件的数据集

  • get(条件),返回单条数据,如果给定的条件超过一条,或者没有数据,都会报错

查询谓词

查询示例:

Book.objects.filter(id__exact=1)

等同于

select * from Book where id = 1

由字段名__谓词组成

  • __exact等值匹配,经常用来匹配是否为空,is null
  • __contains包含指定值查询,**经常使用,**类似于sql中的like ‘%xxx%’
  • __startwith以XXX开始
  • __endwith以XXX结尾
  • __gt大于指定值,常用
  • __gte大于等于指定值,常用
  • __lt小于指定值,常用
  • __lte小于等于指定值,常用
  • __in查找的数据是否在指定范围内,常用,示例Book.objects.filter(id__in = [1,3,5])
  • __range指定区间范围内查询,和sql中的between一样,常用,示例Book.objects.filter(id__range = (20,40))
  • 还有很多…
ORM更新 *** 作

流程:查(使用get)->改->存(使用save)

后端更改 *** 作

单条数据更新的具体示例:

# 单条数据更新
b1 = Book.objects.get(id=1)
b1.price = 22
b1.save()

批量更新,直接将查询结果使用update方法更新

# 批量更新
b1 = Book.objects.filter(price__gt=50)
b1.update(market_price=100)

将所有定价大于50的书市场价均设置为100

通过前端超链接页面更新

前面的图书管理界面上,最后一列“op”绑定了两个空链接,现在先来实现更新功能

一步步来

添加视图函数

views.py中添加的内容

def update_book(request, book_id):
    try:
        book = Book.objects.get(id=book_id)
    except Exception as e:
        print('--update book error is %s' % (e))
        return HttpResponse("书本不存在")
    if request.method == 'GET':
        return render(request, 'bookstore/update_book.html', locals())
    elif request.method == 'POST':
        price = request.POST['price']
        market_price = request.POST['market_price']
        book.price = price
        book.market_price = market_price
        book.save()
        return HttpResponseRedirect('/bookstore/all_book')

这个视图函数还挺复杂的,记录一下逻辑

既然是要更改数据,就说明要像数据库传递数据,那么一般是post请求,但是超链接是get请求,所以实际上要对请求进行判断

  1. 首先,如果是get请求,就跳转到路由指定的模板文件上,并通过local()将get到的局部变量打包成字典,传递给模板文件update_html,模板文件见下面小节(添加模板文件)

  2. 如果是post请求,也就是说要通过页面给后台传递参数了,那么把要实现的功能在这里写好,也就是在这里实现数据的更新

    1. 查数据

    2. 改数据

    3. 存数据

    4. 返回HttpResponse实体,代码中是重定向,指定了路由的相对路径,绝对路径应该是http://127.0.0.1:8000/bookstore/all_book

      强调一下,这里的return必须写,至于是render、HttpResponse还是HttpResponseRedirect根据业务需求,不然项目不知道下一步该往哪里去

  3. 逻辑上已经没什么问题,但有一点要注意,查询book是用的get方法,get方法具有唯一性,极易报错,所以有必要进行异常检测

  4. 视图函数中我传递了一个book_id参数,这点非常重要。首先这个参数是我们要查询的书名,其次,路由中也要用到,下面会讲到

添加模板文件

视图函数完成后,就要进行前端页面的设计了,前面的逻辑是点击超链接(get请求)的话,就去渲染网页文件,并向网页传递book参数

update_html里的内容

DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>更改书籍title>
head>
<body>
<form action="/bookstore/update_book/{{ book.id }}" method="post">
    <p>
        title <input type="text" value="{{ book.title }}" disabled="disabled" />
    p>
    <p>
        publish <input type="text" value="{{ book.pub }}" disabled="disabled" />
    p>
    <p>
        price <input type="text" value="{{ book.price }}" name="price" />
    p>
    <p>
        market_price <input type="text" value="{{ book.market_price }}" name="market_price" />
    p>
    <p>
        <input type="submit" value="更新" />
    p>
form>
body>
html>

看看界面

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YoKmMgAy-1651225916930)(C:\Users\HP\AppData\Roaming\Typora\typora-user-images\image-20220429142029973.png)]

界面还有待优化,这是CSS的事情,暂时不进行美化

很明显是表单数据,因此使用form标签,form的action参数是表单数据请求指向的地址,也就是路由,这个路由的写法也是用的Django语法

注意get请求向这个网页文件传递了book参数,也就是通过book_id获取到的book信息,因此book.id就是数据库中的id字段,title,pub等等也是,4个input标签中各填入了book的4个参数,通过value匹配,用的也是Django的语法,其中title,pub是不可修改的(“disabled” )

最后一个input标签是提交按钮,也就是点击后发送post请求到数据库,并重定向到视图函数中写好的地址

添加路由

目前数据库中是有5本书的,也就是说,如果按绝对路径来定义路由的话,要定义5个路由,分别是

path('update_book/1', views.update_book),
path('update_book/2', views.update_book),
path('update_book/3', views.update_book),
path('update_book/4', views.update_book),
path('update_book/5', views.update_book),

这么写明显不合理,如果有100个,是不是要写100行

这里有多种替代方式

  • 通过正则表达式来传递
  • 通过Django的path转换器来传递

这里使用的是path转换器,用path转换器记得在视图函数中传递参数,这点非常重要,前面就已经提到了,视图函数中传递了一个book_id这个参数,视图函数通过这个book_id进行了一系列的 *** 作,使用path路由的时候也要用到,正确的写法如下

urls.py中的内容

from django.urls import path

from bookstore import views

urlpatterns = [
    path('all_book', views.all_book),
    path('update_book/', views.update_book),
]

添加了一个path('update_book/', views.update_book)

path转换器的精髓就在这里,接收视图函数传递的形参,使用<>来转换,前面是这个数据的类型,后面是参数,有点像java中的泛型

小结2

这里算是完成了一个相对初学者来说,比较复杂的功能,涉及到的技术栈有:

  1. 数据库的更新,包括查-改-存
  2. 视图函数的创建,包括两种不同的请求,重定向等
  3. 如何像模板文件传递参数
  4. 如何通过path转换器指定路由

参数,视图函数通过这个book_id进行了一系列的 *** 作,使用path路由的时候也要用到,正确的写法如下

urls.py中的内容

from django.urls import path

from bookstore import views

urlpatterns = [
    path('all_book', views.all_book),
    path('update_book/', views.update_book),
]

添加了一个path('update_book/', views.update_book)

path转换器的精髓就在这里,接收视图函数传递的形参,使用<>来转换,前面是这个数据的类型,后面是参数,有点像java中的泛型

小结2

这里算是完成了一个相对初学者来说,比较复杂的功能,涉及到的技术栈有:

  1. 数据库的更新,包括查-改-存
  2. 视图函数的创建,包括两种不同的请求,重定向等
  3. 如何像模板文件传递参数
  4. 如何通过path转换器指定路由
ORM删除 *** 作

单条删除:先get(),然后delete()

批量删除:filter()查找,然后delete()删除

伪删除:在表中添加字段is_active,布尔类型,做伪删除时,把值设为false,显示的时候只显示is_active=True的数据,这样就实现了伪删除 *** 作

删除实例

ORM查询中,表格中还有个删除功能没实现,现在补充一下

过程如下

在模型中添加伪删除字段
class Book(models.Model):
    def __str__(self):
        return '%s_%s_%s_%s' % (self.title, self.pub, self.price, self.market_price)

    title = models.CharField("书名", max_length=50, default='', unique=True)
    pub = models.CharField("出版社", max_length=100, default='')
    price = models.DecimalField("定价", max_digits=7, decimal_places=2, default=0.0)
    market_price = models.DecimalField("零售价", max_digits=7, decimal_places=2, default=0.0)
    # info = models.CharField("描述", max_length=100, default='')
    is_active = models.BooleanField('是否活跃', default=True)

添加的是最后一行,后面删除实际上是将这个字段设置为false

视图函数

在views.py中添加以下函数:

def delete_book(request):
    book_id = request.GET.get('book_id')
    if not book_id:
        return HttpResponse('---book_id不存在')
    try:
        book = Book.objects.get(id=book_id, is_active=True)
    except Exception as e:
        print('delete book get error %s' % e)
        # print('--update book error is %s' % (e))
        return HttpResponse("书本不存在")
    book.is_active = False
    book.save()
    return HttpResponseRedirect('/bookstore/all_book')

注意,这里在函数中没有传形参book_id了,说明路由配置不是之前的那种了,这里跟着老师使用了第二种路由配置方式**,就是通过request.GET.get()方法获取请求中的参数,注意,get方法是寻找名为name(本例中为‘book_id’)的GET参数,而且如果参数没有提交,返回一个空的字符串,如果我点击第5条图书,那么请求的链接应该是http://127.0.0.1:8000/bookstore/delte_book?id=5也就是说,我的book_id=5**,这一点的理解很重要,不然会陷入一个理解的死循环,就像月读一样,为什么请求链接是这样的呢,那就要看我的模板文件(all_book.html)了。

模板文件修改

这里的模板文件只涉及all_book.html,因为删除后直接重定向到这个文件了,所以对比上面的更新要简单一点,在模板文件中修改成下面的内容

<td>
	<a href="/bookstore/update_book/{{ book.id }}" >更新a>
	<a href="/bookstore/delete_book?book_id={{ book.id }}" onclick="return confirm('是否确认删除');">删除a>
td>

回到上面的问题,我增加了一个传递参数的符号?,表明后面的book_id是要传的参数,其实上面get()方法得到的参数就传到了这里,也就是超链接应该是bookstore/delete_book?book_id=5

然后再回到视图函数,后面是删除 *** 作,说是删除,其实不如理解为修改,因为执行的是伪删除 *** 作,注意修改后需要save一下

路由添加

注意这里的路由不再是用path转换器了,而是直接传递参数,写法如下

from django.urls import path

from bookstore import views

urlpatterns = [
    path('all_book', views.all_book),
    path('update_book/', views.update_book),
    path('delete_book', views.delete_book),
]

最后一行就是添加的路由,后面通过?传参,理解上相对第一种更麻烦一点,我推荐使用第一种

F对象和Q对象 F对象

先上一下数据库中book表

idtitlepricemarket_pricepubis_active
1python25.0050.00清华大学出版社1
2Django70.0075.00清华大学出版社1
3JQuery90.0085.00机械工业出版社1
4Linux80.0065.00机械工业出版社1
5HTML590.00105.00清华大学出版社1

F对象代表数据库中某条记录的字段的信息,是对数据库中的字段值在不获取的情况下进行 *** 作,可以解决并发的问题,类似于给数据库加锁,把并发事件转为串联事件,第二个作用的用于字段之间的比较,比如比较售价大于定价的书籍,写法如下:

Book.objects.filter(market_price__gt=F('prive'))
Q对象

使用复杂的逻辑或、逻辑非等 *** 作,可以灵活组合

语法

Book.objects.filter(Q(price__lt=20)|Q(pub="清华大学出版社"))

聚合查询和原生数据库 *** 作 聚合查询 普通聚合

聚合函数: Sum Avg Count Max Min

语法:Book.objects.aggregate(结果变量名=Sum(列))

结果变量名就是sql中的查询结果别名

示例:

Book.objects.aggregate(res=Count('id'))

返回:
{res: 4}

分组聚合

语法:QuerySet.annotate(结果变量名=Sum(列))

sql中的group by

示例——统计每个出版社有多少本书:

pub_set = Book.objects.values('pub')
pub_set_count = put_set.annotate(mycount=Count('pub'))
原生数据库 *** 作 raw

语法:Book.objects.raw(sql语句, 拼接参数)

个人对基础的sql语句是比较熟的,因此个人喜欢用原生的数据库 *** 作,按理说也可以用pandas *** 作

但是原生的有很多缺陷和漏洞,SQL注入

解决方法:把参数放入拼接参数中

cursor 游标

语法:

from django.db import connection
with connection.cursor() as cur:
    cur.execute('sql语句', '拼接参数')

还是推荐使用ORM

admin后台管理系统

创建超级用户指令

python manage.py createsuperuser

这里需要设置用户名、邮箱和密码

进入用户管理系统地址

http://127.0.0.1:8000/admin

根据上面设置的用户名密码登录,可以进入Django的后台管理系统

注册自定义的模型类

目前模型文件(models.py)文件中有Book和Author两个类,也就是数据库中的两张表,如果要自己定义的模型类也能在/admin后台管理系统中显示和管理,需要将自己的类注册到后台管理界面

方法步骤:

  1. 在应用的admin.py中导入要注册的模型类,from .models import Book
  2. 调用admin.site.register方法进行注册,如admin.site.register(Book)
模型管理器类

可以为后台管理界面添加便于 *** 作的新功能,继承自django.contrib.admin中的ModelAdmin类

什么意思,就是我们自建的模型展现形式很丑,而自带的展示起来更加美观

使用方法:

  1. 在应用的admin.py里进行定义,class XXXManager(admin.ModelAdmin)

  2. 绑定注册模型管理器和模型类

from django.contrib import admin
from .models import *
# Register your models here.


class BookManager(admin.ModelAdmin):
    list_display = ['id', 'title', 'pub', 'price', 'market_price', 'is_active']


admin.site.register(Book, BookManager)

list_display参数是将数据以表格的形式展示是管理后台上,列表中有几个数据,就展示多少个列

除了list_display参数,还有以下几个常用的参数:

  • list_display_link = [‘title’],修改页面的超链接,默认在ID列上
  • list_filter = [‘pub’] 添加过滤器
  • search_fields = [‘title’] 添加搜索框 模糊查询
  • list_editable = [‘price’, ‘market_price’] 添加可在列表页编辑的字段
Meta类修改属性

模型(models.py)中添加Meta类,见前文**ORM基础字段及选项中**的模型类Meta

示例:

class Book(models.Model):
    def __str__(self):
        return '%s_%s_%s_%s_%s' % (self.title, self.pub, self.price, self.market_price, self.is_active)

    title = models.CharField("书名", max_length=50, default='', unique=True)
    pub = models.CharField("出版社", max_length=100, default='')
    price = models.DecimalField("定价", max_digits=7, decimal_places=2, default=0.0)
    market_price = models.DecimalField("零售价", max_digits=7, decimal_places=2, default=0.0)
    # info = models.CharField("描述", max_length=100, default='')
    is_active = models.BooleanField('是否活跃', default=True)

    class Meta:
        verbose_name = '图书'  # 英文中的单数
        verbose_name_plural = verbose_name  # 英文中的复数,如果不该的话,会在中文字符后面多一个s

关系映射

通常不会把所有的数据都放在同一张表,不易于扩展,如一个学生可以报多个课程,一个课程可以有多个学生报名

一对一映射

语法:OneToOneField(类名,on_delete=xxx),on_delete是级联删除

新概念:

外键——如果A表中的X字段引用了B表中的主键,A表叫做子表,B表叫做主表,X字段称为A表的外键,外键描述的是表之间的关系。级联删除——如员工表中一项数据是部专门ID,部门ID是部门表的主键,如果是级联删除,当删除了部门A的时候,会把所有属于部门A的员工都属给删除。

级联删除on_delete=models.CASCADE实例,步骤,新建应用oto,在设置文件中添加这个应用,然后在oto应用的模型文件中创建author及wife表

from django.db import models

# Create your models here.


class Author(models.Model):
    name = models.CharField('姓名', max_length=11)
    class Meta:
    	verbose_name = '作者'
        verbose_name_plural = verbose_name     


class Wife(models.Model):
    name = models.CharField('姓名', max_length=11)
    author = models.OneToOneField(Author, on_delete=models.CASCADE)  # 级联删除,与Author表共生死
    class Meta:
    	verbose_name = '作者之妻'
        verbose_name_plural = verbose_name     

创建数据:

from oto.models import *
a1 = Author.objects.create(name='hg')  # Author中的表数据创建
w1 = Wife.objects.create(name='zl', author=a1)  # wife表中的数据创建
# 还可以用下面的方式创建外键连接
w1 = Wife.objects.create(name='zl', author_id=a1.id)

正向查询方法:

wife = Wife.objects.get(name='zl')
# 关联到wife的丈夫
wife.author.name

反向查询方法:

author = Author.objects.get(name='hg')
# 关联到author的妻子
author.wife.name

反向查询时,没有外键的一方,可以调用反向属性查询到关联的另一方,author虽然没有wife的外键,但Django在创建Wife模型时,默认创建反向属性

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/866831.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-12
下一篇 2022-05-12

发表评论

登录后才能评论

评论列表(0条)

保存