时间:2022-10-02 12:52:30 | 栏目:Python代码 | 点击:次
本文主要的目的是通过一个简单的例子,展示`get_absolute_url`的用法,抛砖引玉,理解实例方法的本质,能够在不同的业务场景下,灵活多变,完成需求。
环境:Python3.8 + Django3.0
我们都知道,在反向解析url的时候,Django提供了三种方法,帮我们替代硬编码的方式,也就是:
url
模板标签。reverse()
函数。get_absolute_url
方法。前面两种方式比较常见,我们也很熟悉,但是最后的get_absolute_url
方法,可能很多人就不明白具体如何使用了。下面我们通过一个简单易懂的例子,来搞懂它的具体使用方法。
首先,假设我们有下面的学生模型:
class Student(models.Model): sex_choice = [ ('man', '男性'), ('woman', '女性'), ] name = models.CharField(max_length=128) sex = models.CharField(max_length=8, choices=sex_choice) tel = models.PositiveIntegerField() def __str__(self): return self.name
学生包含姓名、性别和电话。
不要忘记makemigrations和migrate。
然后我们接入admin后台,随意手动创建一些学生实例:
from django.contrib import admin from app.models import Student class StudentAdmin(admin.ModelAdmin): list_display = ['name', 'sex', 'tel'] admin.site.register(Student, StudentAdmin)
我们编写了下面的urls:
from django.contrib import admin from django.urls import path from app import views urlpatterns = [ path('admin/', admin.site.urls), path('students/', views.students), path('man/<int:id>/', views.man, name='man'), path('woman/<int:id>/', views.woman, name='woman'), ]
这里的students比较好理解,查看所有的学生列表。但是man和woman两条路由的设计就属于特殊需求了,按理说应该直接一条路由即可,不就是查看某个具体学生的信息嘛。
但如果业务需求是这样的:男生和女生必须使用不同的url进行访问!
那就只能这么分开编写成两条路由了。
注意url中的name属性,用于后面的反向路由解析。
我们编写了下面的视图,很简单:
from django.shortcuts import render from app import models def students(request): s = models.Student.objects.all() return render(request, 'students.html', locals()) def man(request, id): student = models.Student.objects.get(id=id) return render(request, 'student.html', locals()) def woman(request, id): student = models.Student.objects.get(id=id) return render(request, 'student.html', locals())
首先看看student.html
:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <p>name: {{ student.name }}</p> <p>sex: {{ student.sex }}</p> <p>tel: {{ student.tel }}</p> </body> </html>
很简单,就是展示学生的信息,没有需要关注的,仅仅用于表示运行正常,信息显示正确。
重点是students.html
(多了个s,复数形式):
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h4>欢迎访问liujiangblog.com, 学习更多Django教程</h4> {% for student in s %} {% if student.sex == 'man' %} <p> 姓名:{{ student.name }} 详情:<a href="{% url 'man' student.id %}" rel="external nofollow" >{% url 'man' student.id %}</a> </p> {% else %} <p> 姓名:{{ student.name }} 详情:<a href="{% url 'woman' student.id %}" rel="external nofollow" >{% url 'woman' student.id %}</a> </p> {% endif %} {% endfor %} </body> </html>
通过if标签的判断,决定最终生成的url是哪种。这里使用了Django内置的url模板标签语法。
访问students/
页面显示结果:
点击任何一条学生链接都可以正常跳转到详情页面。
上面的代码实现了业务需求,男生和女生自动生成了不一样的url,而不是我们惯例的/student/
,整个过程也很简单,比较好理解。
但是,这里有个不足之处,那就是区分男女生的逻辑放在了HTML模板文件中,这不是个好的做法,也不优雅。
实际上我们可以使用get_absolute_url
方法,在Python代码中实现这一功能。
首先,修改Student模型,添加get_absolute_url
方法:
class Student(models.Model): sex_choice = [ ('man', '男性'), ('woman', '女性'), ] name = models.CharField(max_length=128) sex = models.CharField(max_length=8, choices=sex_choice) tel = models.PositiveIntegerField() def __str__(self): return self.name def get_absolute_url(self): from django.urls import reverse if self.sex == 'man': return reverse('man', args=(self.id,)) else: return reverse('woman', args=(self.id,))
在get_absolute_url
方法中,我们导入了reverse,这是Django提供的反向解析功能。
reverse能避免我们对url进行硬编码,它接收多种类型的参数,可以是一个视图名,也可以是一个url的name。相关的参数通过args传递,这是一个元组,有顺序。
上面的代码中,通过if/else判断,根据性别的不同,解析出男女生对应的url。
然后,在students.html
中,我们就可以修改成下面的样子:
<body> <h4>欢迎访问liujiangblog.com, 学习更多Django教程</h4> {% for student in s %} <p> 姓名:{{ student.name }} 详情:<a href="{{ student.get_absolute_url }}" rel="external nofollow" >{{ student.get_absolute_url }}</a> </p> {% endfor %} </body>
首先,没有if/else模板标签了。其次使用{{ student.get_absolute_url }}
来代替url模板标签。
student是Student模型类的一个实例,它可以访问类中定义的get_absolute_url方法,从而进入if/else判断,然后根据性别的不同,reverse出不同的url字符串,并在HTML模板中展示出来。
整个HTML模板显得更加简洁优雅,最后的页面结果也是完全一样的。实际上,这里也体现出了Django的模型层和模板层的高度配合。
例子很简单,无非就是在Student模型中添加了一个get_absolute_url
方法。但是如果仔细思考一下我们会发现这里面有很多体现语言特点的东西:
Django本身没有实现一个基本的get_absolute_url
方法,在models.Model
中也没有get_absolute_url
方法的影子,所以这个方法其实只是个思路,没有实质。
get_absolute_url
方法本质上只是一个类的实例方法,既然Django内部的代码没有实现它,那么实际上我们可以给它任意命名,比如改成get_url
。你可以试试,它绝对能正常工作。但要小心的是,Django核心源码虽然没有定义get_absolute_url
方法,在admin后台和feed框架等地方却可能使用了这个get_absolute_url
方法,所以在非必须时,不要修改这个方法名。
继续拓展思维,既然可以自定义get_absolute_url
方法,那我可不可以在模型中添加任何我需要的实例方法呢?当然可以!并且这是最强大最灵活的方式!比如根据用户的不同,为模型添加一个user_control
方法,提供不同的信息,控制访问权限,切换页面主题等等。