打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Django学习笔记

初始Django

Django 最初被设计用于具有快速开发需求的新闻类站点,目的是要实现简单快捷的网站开发。Django是一个开放源代码的 Web 应用框架,由Python写成。采用了 MVT 的软件设计模式,即模型 Model,视图 View 和模板 Template。

Django 原理图示

 

 命令 manage

 

命令 manage

# 查看详细命令列表python manage.py# 进入脚本模式python manage.py shell# 创建一个项目django-admin.py startproject project_name# 创建一个 apppython manage.py startapp app_name# 同步数据python manage.py makemigrations # 生成同步信息python manage.py makemigrations web # 指定 apppython manage.py showmigrations --list # 预同步查看python manage.py migrate # 同步python manage.py migrate web # 同步指定 apppython manage.py migrate --database=default-db # 同步指定数据库# 运行网站python manage.py runserverpython manage.py runserver 8001python manage.py runserver 0.0.0.0:8000 # 本机任意 IP,可指定# 完整脚本<path>/python <path>/manage.py runserver 8123# 停止运行pkill -f runserver# 或者ps auxw | grep runserverkill <pid=3424> # 找到显示的 pid# 清空数据,只留下表结构python manage.py flush# 创建管理员python manage.py createsuperuser# 修改用户密码:python manage.py changepassword username

配置 settings

开发环境与测试环境自适应:

# 在项目原 settings.py (删除),创建以下文档结构:settings-- __init__.py-- common.py # 能用配置-- dev.py # 开发环境配置-- pro.py # 生产环境(线上)配置

__init__.py 的内容为:

# 在 mac 和 windows 电脑上为开发环境,生产环境一般为 linuximport socketfrom .common import *host_name_prefix = socket.gethostname().lower()[:3]if host_name_prefix == 'mac' or host_name_prefix == 'win':    from .dev import *else:    from .pro import *

配置内容:

# 站点所在目录BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))# 允许的域名及IP, 线上需要如实填写ALLOWED_HOSTS = [    '*',  # Allow domain and subdomains    # '', # Also allow FQDN and subdomains]# 数据库DATABASES = {    'default': {        'ENGINE': 'django.db.backends.sqlite3',        'NAME': os.path.join(BASE_DIR, 'data_db.sqlite3'),    }}# 时间格式、语言LANGUAGE_CODE = 'en-us'TIME_ZONE = 'Asia/Shanghai'DATETIME_FORMAT = 'Y-m-d H:i:s'USE_I18N = FalseUSE_L10N = FalseUSE_TZ = True# 静态目录STATICFILES_DIRS = [    os.path.join(BASE_DIR, "static"),    os.path.join(BASE_DIR, "file"),]# 媒体文件MEDIA_ROOT = os.path.join(BASE_DIR, "file")MEDIA_URL = "/file/"  # "//xx.xx.com/" dev# 静态文件STATIC_URL = "/static/"

在项目中如果需要引用配置变量:

from django.conf import settings# 使用媒体文件的目录path_prefix = settings.MEDIA_ROOT

模型 models

模型结构:

from django.db import modelsfrom django.urls import reverseimport django.utils.timezone as timezoneclass Item(models.Model):    id = models.AutoField(primary_key=True)    title = models.CharField(max_length=200, blank=False, )    slug = models.SlugField(max_length=50, unique=True, db_index=True)    class Meta:        db_table = "my_item" # 建议给每个 app 增加统计的前缀        verbose_name = 'Item'        ordering = ('id',)    # 自定义属性方法    def tag_list(self):        return [i for i in str(self.tags).split('#')]    def get_absolute_url(self):        return reverse("item_detail", args=[str(self.slug)])    def __str__(self):        return str(self.title)

字段:

# 主键,自增 IDid = models.AutoField(primary_key=True)# slugslug = models.SlugField(max_length=50, default=default_slug,                        unique=True, db_index=True)# 短正数字dy = models.SmallIntegerField(blank=True, null=True)# 整数qty = models.PositiveSmallIntegerField(blank=True, null=True, )# 带小数num = models.DecimalField(blank=True, null=True,    decimal_places=2, max_digits=6)# 其他数字qty = models.IntegerField(blank=True, null=True)qty = models.BigIntegerField(blank=True, null=True)# 文本des = models.CharField(max_length=250, blank=True, null=True)# 长文本text = models.TextField(blank=True, null=True)# 单选gender = models.NullBooleanField(choices=((True, "男"),                                          (False, "女"),                                          (None, "")),                                 default=True)# 布尔型published = models.BooleanField(default=True)# 时间, 默认为创建时间created = models.DateTimeField('Created', auto_now_add=True,                               auto_now=False, editable=False,                               null=True)# 时间, 自动取更新时间update_time = models.DateTimeField(auto_now=True, null=True)# 可编辑时间, auto_now/auto_now_add 为 True 不起作用begin_time = models.DateTimeField(default=timezone.now, editable=True)# 图片上传pic = models.ImageField(upload_to='pic/%Y/%m', blank=True, null=True)# 文件上传file = models.FileField(upload_to='up/%Y/%m', blank=True, null=True)# 外键item = models.ForeignKey("Item", on_delete=models.PROTECT,                         blank=True, null=True)# 外键及选择约束type = models.ForeignKey("Choice", default=4,                         limit_choices_to={'type': 'text_type'},                         related_name=' ', on_delete=models.PROTECT)

查询结果集:

# filter 方法, 返回所有条件为均为真 and 的结果集Entry.objects.filter()# exclude 方法, 返回排除满足所有条件 or 的结果集Entry.objects.exclude()# get()  方法, 返回满足条件的唯一结果, 如结果为多个报错e = Entry.objects.get(id=5)e.title # 直接取字段值, 下条效果相同Entry.objects.select_related('title').get(id=5) # 直接返回指定字段# 不存在的错误对象from django.core.exceptions import ObjectDoesNotExist# extra() 灵活调用实现 SQLEntry.objects.extra(select={'is_recent': "pub_date > '2006-01-01'"})Entry.objects.extra(where=["foo='a' OR bar = 'a'", "baz = 'a'"])Entry.objects.extra(where=['headline=%s'], params=['Lennon'])q.extra(order_by = ['-is_recent'])# dates() 获得结果集中的时间列表(字段,精确度), 如 datetime.date(2005, 1, 1)Entry.objects.dates('pub_date', 'day', order='DESC')

结果集处理:

# 返回空查询结果集Entry.objects.none()# 返回当前结果集的副本Entry.objects.all()# 将 qs1 和 qs2、qs3 联接组合起来qs1.union(qs2, qs3)# 取结果集的交集, 共同的部分qs1.intersection(qs2, qs3)# 取结果集的差集, 仅在 qs1 中有的qs1.difference(qs2, qs3)# 指定取出信息, 提高性能, 可同时使用Entry.objects.defer("des", "body") # 除了这两个字段Person.objects.only("name") # 只取此字段

其他:

# 使用指定的数据库Entry.objects.using('db2')# 使用原生 SQL 语句Person.objects.raw('SELECT * FROM myapp_person')# 联合使用Entry.objects.filter().exclude().get()# 聚合 aggregate()Blog.objects.aggregate(Count('entry'))

结果集查询方法:

# 迭代for e in Item.objects.all():    print(e.title)# 支持切片my_queryset.all()[:5]# 转为列表list(my_queryset.all())# 是否存在查询结果my_queryset.filter(pk=entry.pk).exists()# 获取指定一条结果my_queryset.first() # 第一个结果my_queryset.last() # 最后一个结果, 另有 latest() earliest()# 排序my_queryset.filter.order_by('-pub_date', 'age') # 负号表示降序my_queryset.order_by('?') # 随机排序my_queryset.order_by('blog__name', 'title') # blog__name 中 blog 为外键my_queryset.order_by('title').order_by('pub_date') # 两次排序my_queryset.reverse() # 按相反的顺序返回# 去重my_queryset.distinct()# 结果数my_queryset.count()# 给定列表对应字段的结果, 默认为主键my_queryset.in_bulk([1, 2])my_queryset.in_bulk(['lily', 'tom'], field_name='name')# 获取时间列表, datetimes() 类似my_queryset.dates('pub_date', 'day', order='DESC') # 逻辑使用同上文# exclude 排除# 与 filter 相同, 取非P.objects.exclude(name__contains="tlp")

返回数据格式:

# 返回指定字段值, 类 list [(),()]my_queryset.values_list() # 返回所有字段my_queryset.values_list('id', flat=True) # 单个字段返回此字段所有值组成的列表my_queryset.values_list('id', 'title', named=True) # 返回一个 namedtuple# 返回指定字段值, 类 json [{},{}]my_queryset.values() # 返回所有字段my_queryset.values('blog_id') # blog 为外键, 返回 key 为 blog_idmy_queryset.values('id', 'name')from django.db.models.functions import Lowermy_queryset.values(lower_name=Lower('name')) # 字段名指定, 值转小写# 返回指定类型数据# 返回字典 {'user_name': 'lily', ...}Tweet.objects.values("user_name")# json 和 xmlfrom django.core import serializersdata = serializers.serialize("xml", SomeModel.objects.all())serializers.serialize('json', [book1, book2], indent=2,     use_natural_foreign_keys=True, use_natural_primary_keys=True)from django.core.serializers import serializeserialize('json', SomeModel.objects.all(), cls=LazyEncoder)

Q():

# Q(), 对象的复杂查询# 注: 与字段同时查询时放在前, 不可使用__双下划线from django.db.models import QModel.objects.filter(x=1, y=2) #  字段查询方法Model.objects.filter(Q(x=1) & Q(y=2)) # ANDModel.objects.filter(Q(x=1, z=4) | Q(y=2)) # ORModel.objects.filter(~Q(name="cox")) # NOT

F() 表达式:

# F() 表达式, 对象中某列值的操作# 注: 与字段同时查询时放在前, __双下划线需在一个model里from django.db.models import F# 原价加10, 更新数据, 支持单个和多个E.objects.update(price=F("price") 10)# 查询语文成绩大于数学成绩减5分的学生E.objects.filter(chinese__gt=F('math')-5)E.objects.filter(authors__name=F('blog__name'))E.objects.filter(mod_date__gt=F('pub_date')   timedelta(days=3))

aggregate:

# aggregate 聚合函数# 定义 avg, 值为字段 math 的平均数, 返回: {'avg': 27}from django.db.models import Count, Avg, Sumfrom django.db.models import FloatFieldP.objects.aggregate(avg=Avg('math'))# {'price__avg': 43.54}Book.objects.all().aggregate(Avg('price'))# {'page_price': 0.4470664529184653}Book.objects.all().aggregate(page_price=Avg(F('price') / F('pages'),                                            output_field=FloatField()))

annotate:

# annotate 在 聚合 aggregate 基础上 GROUP BY# 统计每个班最小的年龄l = S.objects.all().annotate(min_age=Min("age"))[(i.class, i.min_age) for i in l]# Fun 自定义类,继承 Func 类, 处理数据from django.db.models import Funclass Lower(Func):  # 继承Func类    function = 'LOWER'  # 使用类属性指定要使用的方法    pass # 细节得看官方文档qs.annotate(field_lower=Func(F('field'), function='LOWER'))# https://docs.djangoproject.com/en/3.0/ref/models/expressions

字段(穿透)查询 lookups:

# 简单匹配Entry.objects.get(id__exact=14) # 精确匹配 sql =Blog.objects.get(name__iexact='beatles blog') # 模糊匹配 sql likeE.objects.get(headline__contains='Lennon')  # 包含 sql LIKE '%Lennon%'E.objects.get(headline__icontains='Lennon')  # sql ILIKE '%Lennon%'E.objects.filter(headline__startswith='Lennon') # 开头包含 LIKE 'Lennon%'E.objects.filter(headline__istartswith='Lennon') # ILIKE 'Lennon%'E.objects.filter(headline__endswith='Lennon') # 结尾包含 LIKE '%Lennon'E.objects.filter(headline__iendswith='Lennon') # ILIKE '%Lennon'E.objects.filter(id__in=[1, 3, 4]) # sql IN (1, 3, 4)E.objects.filter(headline__in='abc') # IN ('a', 'b', 'c')# 图片 ImageField 为空的查询E.objects.filter(picture__exact='')# 条件, 数据和时间# gt 大于; gte 大于等于; lt 小于; lte 小于等于;E.objects.filter(id__gt=4) # 大于 sql id > 4# 是否为空筛选Entry.objects.filter(pub_date__isnull=True)# 正则匹配Entry.objects.get(title__regex=r'^(An?|The)  ')Entry.objects.get(title__iregex=r'^(an?|the)  ')# 关联外键查询Blog.objects.filter(entry__authors__name='Lennon')

结果集操作方法:

# 构造一个空的对象实例Person.objects.none()# 增加信息Person.objects.create(first_name="Bruce", last_name="Springsteen")# 增加信息 2p = Person(first_name="Bruce", last_name="Springsteen")p.save(force_insert=True)# 先查询, 无结果创建一条信息, 返回 [obj, created]Person.objects.get_or_create()# 批量创建, 批量更新 bulk_update() 略Entry.objects.bulk_create([    Entry(headline='This is a test'),    Entry(headline='This is only a test'),    ])# 更新, 返回的更新条数Entry.objects.filter(pub_date__year=2010).update(comments_on=False)# 更新方法 2e = Entry.objects.get(id=10)e.comments_on = Falsee.save()# 有则更新, 无刚创建Person.update_or_create()# 删除b = Blog.objects.get(pk=1)Entry.objects.filter(blog=b).delete()# 输出解释信息 explainprint(Blog.objects.filter(title='My Blog').explain(verbose=True))p.delete() # 删除本数据P.objects.all().delete() # [危险]全删除

实践技巧:

# 查看实际 SQLprint(queryset.query)# 随机返回一条信息Play.objects.filter().order_by('?').first()# 链式查询: Q 查询 OR, 赋默认值, 多重排序, 取第一条obj = (Artist       .objects       .filter(Q(bm=now.month, bd=now.day, type__id=5) |               Q(dm=now.month, dd=now.day, type__id=5))       .extra(select={"null_rating": "rating is null"},              order_by=['null_rating', '-rating', 'id'])       .first()       )

时间相关

from django.utils import timezone# 当前本地时间now = timezone.localtime(timezone.now())now.month # 取年月日等# 查询几天内内容ago_3_days = now - timezone.timedelta(days=3)E.objects.filter(published=1, pub_date__gte=ago_3_days)# 在两个时间内的内容E.objects.filter(begin_time__lte=now, end_time__gt=now)# 将文本解析为时间from django.utils.dateparse import parse_datetimetime = parse_datetime("2012-02-21 10:28:45")# 时间范围start_date = datetime.date(2005, 1, 1)end_date = datetime.date(2005, 3, 31)E.objects.filter(pub_date__range=(start_date, end_date))# 时间条件my_queryset.filter(pub_date__date=datetime.date(2005, 1, 1)) # 在指定时间my_queryset.filter(pub_date__date__gt=datetime.date(2005, 1, 1)) # 时间之后my_queryset.filter(pub_date__year=2005) # 在某年内# 支持 year/month/day/week_day/time/hour 等my_queryset.filter(pub_date__hour__gte=20) # 在时间后

视图 views

基本视图:

from django.shortcuts import render, get_object_or_404from web.models import Itemdef item_detail(request, slug):    item = Item.objects.get(slug=slug, published=1)    return render(request, 'item.html', {'item': item})    # return render(request, 'i.html',     # {'item': get_object_or_404(Item, slug=slug, published=1)    # })

返回基础内容及跳转:

from django.shortcuts import HttpResponse, HttpResponseRedirectfrom django.contrib.auth.decorators import login_required@login_required(login_url='/login/') # 页面需要登录def item_detail(request, slug):    try:        pass        return HttpResponse('获取成功!') # 返回内容    except:        HttpResponseRedirect('/login/') # 跳转

其他:

# 301 跳转from django.shortcuts import HttpResponsePermanentRedirectdef photo_item(request, slug):    return HttpResponsePermanentRedirect('/node/{0}'.format(str(slug)))

路由 urls

from django.contrib import adminfrom django.urls import include, path, re_pathfrom web import viewsurlpatterns = [    path('', views.index, name='index'),    path('item/<slug:slug>', views.item_detail, name='item_detail'),    path('login/', views_user.login_site, name='login'),    # api    path('api/', include('api._urls')),    # admin    path('admin.noname/', admin.site.urls),]

缓存 cache

配置:

# CachesCACHES = {    'default-no': {    # 不执行, 开发模式下可使用        'BACKEND': 'django.core.cache.backends.dummy.DummyCache',        'LOCATION': os.path.join(BASE_DIR, 'cache'),        'TIMEOUT': 60 * 60 * 24 * 7,  # a week        'OPTIONS': {            'MAX_ENTRIES': 10000        }    },    'default': {   # Memcached        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',        'LOCATION': '127.0.0.1:11211',        'TIMEOUT': 60 * 60 * 24 * 7,  # a week    },    'file': { # 文件         'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache',        'LOCATION': os.path.join(BASE_DIR, 'cache'),        'TIMEOUT': 60 * 60 * 24 * 7,  # a week        'OPTIONS': {            'MAX_ENTRIES': 10000        }    },}

在视图 views.py 中使用

from django.views.decorators.cache import cache_page@cache_page(60*10, cache='file') # unit of second, 10mindef index(request):    pass

在路由 urls.py 中使用

from django.views.decorators.cache import cache_pageurlpatterns = [path('node',     cache_page(60*2, cache="default")(node.web),     name='node'),]

写一个缓存装饰器

# todo

模板

基础功能:

# 取模型对象的属性{{ item.title }}# 媒体地址{{ MEDIA_URL }}# 引用模板{% include "footer.html" %}# 给引用模板传入变量{% include "pub/m_review.html" with type='wiki' cell=wik.slug %}# 当前时间{% now "Y" %} # 格式如:2020# 取其对应外键中的条目{% for i in teacher.class_set.all %}

流程控制:

# if 语句{% if item.switch_on %}    开启{% else %}    关闭{% endif %}# for 中的计算{% if i.type.item == 'var' %} # > < != or and# for 循环, 处理首个元素{% for i in item.tag_list %}    {% if forloop.first %}标签:{% endif %}<code>{{ i }}</code>{% empty %}   no msg.{% endfor %}

信息过滤处理:

#  按 html 代码输出{{ i.html|safe }}# 取 type 的对外显示内容i.get_type_display }}# 取外键的属性{{ i.teacher.name }}# 切片计算{% for i in names|slice:":1" %} # 第一个{% for i in names|slice:":4" %} # 前4个# 默认值{{ i.year|default:"2019" }}# 排序{% for i in timeline|dictsort:"time" %} # 正序{% for i in timeline|dictsortreversed:"time" %} # 降序# 显示前12个字符,剩余用「...」代替{{ item.nameCN|truncatechars:12 }}# 时间格式化{{ i.pub_time|date:'Y-m-d' }} # 如:2020-01-05{{ i.pub_time|date:"H:i:s" }} # 如:12:11:04# 取长度{{ name|length }} # 返回如:3# 复合处理{% for i in teacher.class_set.all|dictsortreversed:"id"|slice:":6" %}# 格式化为标题{{ i.name|title }}# 值减去1, 加为正{{ evt.ty|add:-1 }}# 去掉渲染后 html 中的空格{% spaceless %} code {% endspaceless %}# 注释{% comment %} code {% endcomment %} 和 {# code #}{{ foo|truncatechars:7 }} # 显示部分长度, 用省略号代码{{ foo|truncatechars_html:7 }} # 针对 html 显示部分长度

分组:

# 将学生名单按年龄分组,同年龄的显示在一起{% regroup students|dictsortreversed:"time_int" by age as aged %}<ul>{% for age in aged %}    <li>{{ age.grouper }}    <ul>        {% for i in age.list %}          <li>{{ i.name }}: {{ i.height }}</li>        {% endfor %}    </ul>    </li>{% endfor %}</ul>

后台 admin

# 在 url 中增加路由from django.urls import pathfrom django.contrib import adminurlpatterns = [path('web-admin/', admin.site.urls), ]# 在 app 目录中创建 admin.pyfrom django.contrib import adminfrom .models import Newsclass NewsAdmin(admin.ModelAdmin):    list_display = ('id', 'title','tags' ,'url', 'update_time')    raw_id_fields = ('writer', )    list_display_links = ('id', 'title',)    search_fields = ['title', 'writer__name', 'slug']    form = NewsForm # 可定义表单样式    radio_fields = {"type": admin.HORIZONTAL, }  # VERTICAL    list_per_page = 30admin.site.register(News, NewsAdmin)

数据库支持 DB

TODO

富文本管理器

推荐使用 django-ckeditor

升级 django 3.0 后 ckeditor 无法返回上传文件路径问题的解决:

CKEditor Refused to display 'XXX' in a frame because it set 'X-Frame-Options' to 'deny'.

Django 3.0 需要将 X_FRAME_OPTIONS 的默认值由 SAMEORIGIN 调整为 DENY.

# https://docs.djangoproject.com/en/3.0/ref/clickjacking/# settings:MIDDLEWARE = [    ...    'django.middleware.clickjacking.XFrameOptionsMiddleware',    ...]# 给定配置X_FRAME_OPTIONS = 'SAMEORIGIN'

文件图片

TODO

日志

TODO

信号

TODO

登录退出

TODO

特殊字段

ArrayField

支持级数、列表,包括嵌套形式数据,常用场景为文章标签、经纬度等,为 PostgreSQL 所支持, 数据类型如 character varying[]

from django.contrib.postgres.fields import ArrayField, JSONField# 可用 IntegerField 等作为数组内容,格式如 [1, 3, 5, 7],tags = ArrayField(models.CharField(max_length=10, null=True), blank=True, null=True)# 格式如 [[1,3], [2,4]], 限定每个元素里有两个元素(必须)tags = ArrayField(ArrayField(models.CharField(max_length=10), size=2), null=True)# 表单, 上例对应。每个元素编辑和显示时可以用 # 号分隔from django.contrib.postgres.forms import SimpleArrayFieldtags = SimpleArrayField(forms.CharField(), delimiter='#', required=False)tags = SimpleArrayField(SimpleArrayField(forms.CharField()), delimiter='#', required=False)# 查询Tags.objects.filter(tags__contains=['a', 'b'])  # 包含所有元素Tags.objects.filter(tags__contained_by=['a', 'b'])  # 所有内容为其的子集Tags.objects.filter(tags__overlap=['a'])  # 包含其中一个Tags.objects.filter(tags__len=1)  # 长度为1的Tags.objects.filter(tags__0='a')  # 第一个元素为 a 的Tags.objects.filter(tags__1__iexact='b')Tags.objects.filter(tags__0_2__contains=['c'])  # 第1和2和元素中包含 c 的

JSONField

可以存储 json 格式数据,为 PostgreSQL 所支持。

data = JSONField()  # 在 models 中定义Dog.objects.filter(data__owner__name='Bob')  # 指定 owner.name 值Dog.objects.filter(data__owner__other_pets__0__name='Fishy')  # 第一条数据值Dog.objects.filter(data__contains={'owner': 'Bob'})Dog.objects.filter(data__contained_by={'breed': 'collie', 'owner': 'Bob'})Dog.objects.filter(data__has_key='owner')Dog.objects.filter(data__has_any_keys=['owner', 'breed'])Dog.objects.filter(data__values__contains=['collie'])Dog.objects.filter(data__keys__overlap=['breed', 'toy'])

分页

from django.core.paginator import Paginator, EmptyPage, PageNotAnIntegerpic_list = Picture.objects.filter(photo_type=1).order_by('id')paginator = Paginator(pic_list, 1)  # Show Qty contacts per pagepage = request.GET.get('page')try:    pics = paginator.get_page(page)except PageNotAnInteger:    # If page is not an integer, deliver first page.    pics = paginator.get_page(1)except EmptyPage:    # If page is out of range (e.g. 9999), deliver last page of results.    pics = paginator.get_page(1)  # (paginator.num_pages)# 以模板中可以调用, 如 {{ pics.number }}# pics.has_previous 是否有上一页# pics.has_next 是否有下一页# pics.number 当前页序# pics.paginator.num_pages 总页数# pics.previous_page_number 上一页页序# pics.next_page_number 下一页页序
# 设置 cookie, 有效期一年, 单位秒response = self.get_response(request)response.set_cookie("uuid", 'cookie值', max_age=365 * 24 * 60 * 60)# 读取 Cookierequest.COOKIES['uuid'] # 取指定 cookie 值request.COOKIES.keys() # 取所有 cookie 名

TODO

接口 api

简单的接口实现:

from django.http import JsonResponsefrom music.models import Newsdef single_object(obj):    return {        'title': obj.title,        'slug': obj.slug,        'type': obj.type,        'files': ['https://pic.gairuo.com/file/'   i for i in obj.file],        'text': obj.text,    }def news_detail(request):    slug = request.GET['slug']    new = News.objects.get(slug=slug).id    news_data = [single_object_score(obj) for obj in new]    data = {'data': news_data}    return JsonResponse(data, safe=False)

其他

TODO

 来源:https://www.icode9.com/content-4-802201.html
本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
【Python】django模型models的外键关联使用
ModelViewSet+ModelSerializer使用
python测试开发django-75.ORM根据日期查询(__range)
Django Full Coverage(飞速入门)
django QuerySet里那些常用又不常见的技巧
python models操作
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服