get_absolute_url方法详解

博主     2020年08月15日    分类: Django   阅读:4085     评论:0

环境:Python3.8 + Django3.0

我们都知道,在反向解析url的时候,Django提供了三种方法,帮我们替代硬编码的方式,也就是:

  • 在模板中:使用url模板标签。
  • 在Python代码中:使用reverse()函数。
  • 在更高层的与处理Django模型实例相关的代码中:使用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

我们编写了下面的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())

四、HTML模板

首先看看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 }}   &nbsp;&nbsp;&nbsp;&nbsp;
            详情:<a href="{% url 'man' student.id %}">{% url 'man' student.id %}</a>
        </p>
    {% else %}
        <p>
            姓名:{{ student.name }}   &nbsp;&nbsp;&nbsp;&nbsp;
            详情:<a href="{% url 'woman' student.id %}">{% url 'woman' student.id %}</a>
        </p>
    {% endif %}    
{% endfor %}

</body>
</html>

通过if标签的判断,决定最终生成的url是哪种。这里使用了Django内置的url模板标签语法。

访问students/页面显示结果:

点击任何一条学生链接都可以正常跳转到详情页面。

五、使用get_absolute_url方法

上面的代码实现了业务需求,男生和女生自动生成了不一样的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 }}   &nbsp;&nbsp;&nbsp;&nbsp;
        详情:<a href="{{ student.get_absolute_url }}">{{ 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方法,提供不同的信息,控制访问权限,切换页面主题等等。

本文主要的目的是通过一个简单的例子,展示get_absolute_url的用法,抛砖引玉,理解实例方法的本质,能够在不同的业务场景下,灵活多变,完成需求。更多内容请访问我的官方网站https://www.liujiangblog.com


评论总数: 0