06--功能開發--動態釋出頁、首頁、動態詳情頁02
阿新 • • 發佈:2022-05-24
day07 功能開發
1.釋出邏輯
1.1 小程式
-
選圖片
-
填內容
-
提交-資料格式
// v1.0 { // cover欄位: 讓前端自己處理傳過來,不要後端切片自己構造儲存。理由:1.省事 2.節約伺服器操作 cover:"https://mini-1251317460.cos.ap-chengdu.myqcloud.com/08a9daei1578736867828.png", content:"小程式開發太簡單了", // topic欄位: 話題id topic:1, address:"北京市", // user欄位: 注意:不能用id、phone等傳遞,必須使用token能唯一校驗的資料 (因為id和phone容易被人偽造構建資料) user:"token欄位", images:[ "https://mini-1251317460.cos.ap-chengdu.myqcloud.com/08a9daei1578736867828.png", "https://mini-1251317460.cos.ap-chengdu.myqcloud.com/08a9daei1578736867828.png" ] } // v2.0 為了方便後續的images管理(刪除等操作),應當將檔名也傳遞過來 { cover:"https://mini-1251317460.cos.ap-chengdu.myqcloud.com/08a9daei1578736867828.png", content:"小程式開發太簡單了", address:"北京市", topic:1, images:[ { path:"https://mini-1251317460.cos.ap-chengdu.myqcloud.com/08a9daei1578736867828.png", cos_key:"08a9daei1578736867828.png" }, { path:"https://mini-1251317460.cos.ap-chengdu.myqcloud.com/08a9daei1578736867828.png", cos_key:"08a9daei1578736867828.png" }, ] }
1.2 API邏輯
from rest_framework.views import APIView from rest_framework.generics import CreateAPIView from rest_framework import serializers from apps.api import models class NewsDetailSerializer(serializers.Serializer): key = serializers.CharField() cos_path = serializers.CharField() class NewsModelSerializer(serializers.ModelSerializer): images = NewsDetailModelSerializer(many=True) class Meta: model = models.News fields = "__all__" class NewsView(CreateAPIView): """ 建立動態的API """ serializer_class = NewsModelSerializer
class News(models.Model): """ 動態 """ # 這五項必傳 cover = models.CharField(verbose_name='封面', max_length=128) content = models.CharField(verbose_name='內容', max_length=255) topic = models.ForeignKey(verbose_name='話題', to='Topic', null=True, blank=True) address = models.CharField(verbose_name='位置', max_length=128, null=True, blank=True) user = models.ForeignKey(verbose_name='釋出者', to='UserInfo', related_name='news') # 這四項可不傳 favor_count = models.PositiveIntegerField(verbose_name='贊數', default=0) viewer_count = models.PositiveIntegerField(verbose_name='瀏覽數', default=0) comment_count = models.PositiveIntegerField(verbose_name='評論數', default=0) create_date = models.DateTimeField(verbose_name='建立時間', auto_now_add=True) class NewsDetail(models.Model): """ 動態詳細 """ key = models.CharField(verbose_name='騰訊物件儲存中的檔名', max_length=128, help_text="用於以後在騰訊物件儲存中刪除") cos_path = models.CharField(verbose_name='騰訊物件儲存中圖片路徑', max_length=128) news = models.ForeignKey(verbose_name='動態', to='News')
1.3 規則
{
k1:v1,
k2:v2,
k3:{...},
k4:[
{....}
]
}
# 總結:k1,k2形式的,可直接欄位儲存;若是k3、k4這種,可考慮子序列化(巢狀序列化)進行校驗或儲存
2.restful api回顧
2.1 APIView ( 可以 )
# 自己根據邏輯,直接寫
from rest_framework.response import Response
class UserModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.UserInfo
fields = "__all__"
class UserView(APIView):
def get(self,request,*args,**kwargs):
user_list = models.UserInfo.objects.all()
ser = UserModelSerializer(instance=user_list,many=True)
return Response(ser.data)
def post(self,request,*args,**kwargs):
ser = UserModelSerializer(data=request.data)
if ser.is_valid():
# models.UserInfo.objects.create(**ser.validated_data)
ser.save(user_id=1)
return Response(ser.data)
return Response(ser.errors)
2.2 ListAPIView
ListAPIView,CreateAPIView,RetrieveAPIView,UpdateAPIView,DestroyAPIView
class NewTestModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.News
fields = "__all__"
class NewTestView(CreateAPIView,ListAPIView):
serializer_class = NewTestModelSerializer
queryset = models.News.objects.filter(id__gt=4)
2.2.1 使用者傳遞某些值
建立使用者時,自己在後臺生成一個UID。
class NewTestModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.News
fields = "__all__"
class NewTestView(CreateAPIView,ListAPIView):
serializer_class = NewTestModelSerializer
queryset = models.News.objects.filter(id__gt=4)
def perform_create(self, serializer):
serializer.save(uid=str(uuid.uuid4())) # save()時 自己額外傳遞
2.2.2 fields和exclude的區別?
通過fields和exclude定製頁面展示部分欄位資料。
需求:只顯示使用者表的id,name,age的資料,其他不顯示。
class NewTestModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.News
# fields = ["id","name",'age']
exclude = ['gender']
class NewTestView(ListAPIView):
serializer_class = NewTestModelSerializer
queryset = models.User.objects.all()
[
{id:1,name:'xxx',age:18},
{id:1,name:'xxx',age:11},
{id:1,name:'xxx',age:99},
]
需求:資料庫有5個欄位,顯示7個欄位。
class NewTestModelSerializer(serializers.ModelSerializer):
xx = serializers.CharField(source='id')
x1 = serializers.SerializerMethodField()
class Meta:
model = models.News
# fields = "__all__" # 為all時,是model的欄位,加自定義的欄位
# fields = ['id','name','age','gender','phone','xx','x1']
exclude = ['id','name']
def get_x1(self,obj):
return obj.id
class NewTestView(ListAPIView):
serializer_class = NewTestModelSerializer
queryset = models.User.objects.all()
[
{id:1,name:'xxx',age:18... xx:1,x1:1},
{id:2,name:'xxx',age:11... xx:2,x1:2},
{id:3,name:'xxx',age:99, xx:3,x1:3},
]
2.2.3 read_only
新增時不要,檢視時候需要。
需求:編寫兩個介面 新增(3欄位)、獲取列表(5個欄位)
class NewTestModelSerializer(serializers.ModelSerializer):
# phone = serializers.CharField(source='phone',read_only=True)
# email = serializers.CharField(source='email',read_only=True)
class Meta:
model = models.News
fields = "__all__"
read_only_fields = ['phone','email',]
class NewTestView(CreateAPIView, ListAPIView):
serializer_class = NewTestModelSerializer
queryset = models.User.objects.all()
新增:
{
name:'xx',
age:'19',
gender:1
}
獲取:
[
{name:'xx',age:'xx',gender:'',phone:'xx',email:xxx}
]
2.3.4 複雜需求
新增時用一個serializers、列表時用一個serializers
class NewTestModelSerializer1(serializers.ModelSerializer):
class Meta:
model = models.News
fields = "__all__"
class NewTestModelSerializer2(serializers.ModelSerializer):
class Meta:
model = models.News
fields = "__all__"
class NewTestView(CreateAPIView, ListAPIView):
queryset = models.User.objects.all()
# 重寫get_serializer_class()
def get_serializer_class(self):
if self.request.method == 'POST':
return NewTestModelSerializer1
if self.request.method == 'GET':
return NewTestModelSerializer2
2.3.5 serializers巢狀
class CreateNewsTopicModelSerializer(serializers.Serializer):
key = serializers.CharField()
cos_path = serializers.CharField()
class CreateNewsModelSerializer(serializers.ModelSerializer):
imageList = CreateNewsTopicModelSerializer(many=True)
class Meta:
model = models.News
exclude = ['user', 'viewer_count', 'comment_count',"favor_count"]
def create(self, validated_data):
# 把imageList切走
image_list = validated_data.pop('imageList')
# 建立New表中的資料
news_object = models.News.objects.create(**validated_data)
data_list = models.NewsDetail.objects.bulk_create(
[models.NewsDetail(**info, news=news_object) for info in image_list]
)
news_object.imageList = data_list
if news_object.topic:
news_object.topic.count += 1
news_object.save()
return news_object
class NewsView(CreateAPIView):
"""
釋出動態
"""
serializer_class = CreateNewsModelSerializer
def perform_create(self, serializer):
# 只能儲存:News表中的資料()
# 呼叫serializer物件的save(先呼叫create)
new_object = serializer.save(user_id=1)
return new_object
3. 首頁展示
- 小程式
- 初始化
- 下拉重新整理
- 上翻頁
- 瀑布流
- 後端API
- APIView
- ListAPIView
- filter:最大/最小過濾
- pagination:定製返回資料條數
3.1 小程式-瀑布流
瞭解,能實現就行。
以下三種,主要了解思路,具體實現可試著自己找元件。
3.1.1 方式一:column-count
# 通過css的column-count,自動展示瀑布流
缺點:資料填充是先左列 從上到下填充完,再下一列 從上到下
# wxml:
<view class="container">
<view class="item">
<image src="https://hbimg.huabanimg.com/fw236" mode="widthFix" ></image>
</view>
<view class="item">
<image src="https://hbimg.huabanimg.com/fw236" mode="widthFix" ></image>
</view>
<view class="item">
<image src="https://hbimg.huabanimg.com/fw236" mode="widthFix" ></image>
</view>
<view class="item">
<image src="https://hbimg.huabanimg.com/fw236" mode="widthFix" ></image>
</view>
</view>
# css:
.container {
/* 元素被劃分的列數 */
-moz-column-count:2; /* Firefox */
-webkit-column-count:2; /* Safari and Chrome */
column-count:2;
/* 規定列間的間隔為 20個相對畫素:*/
-moz-column-gap:20rpx; /* Firefox */
-webkit-column-gap:20rpx; /* Safari and Chrome */
column-gap:20rpx;
}
.container .item{
/ *在多列布局頁面下的內容盒子如何中斷*/
break-inside: avoid-column; // 避免在元素內分列
-webkit-column-break-inside: avoid; /* Safari and Chrome */
}
3.1.2 方式二:flex佈局-橫向 + 判斷奇偶 (√)
# flex-橫向 + 寬度50% => 實現兩列 + for迴圈,判斷資料
# wxml:
<view class='container'>
<view class="item">
<view wx:for="{{dataList}}" wx:key="id">
<view wx:if="{{index % 2 == 0}}">
<view>{{item.id}}</view>
<image src="{{item.img}}" mode="widthFix"></image>
</view>
</view>
</view>
<view class="item">
<view wx:for="{{dataList}}" wx:key="id">
<view wx:if="{{index % 2 !== 0}}">
<view>{{item.id}}</view>
<image src="{{item.img}}" mode="widthFix"></image>
</view>
</view>
</view>
</view>
# css:
.container {
display: flex;
flex-direction: row;
}
.container .item{
width: 50%;
overflow: hidden;
}
.container .item image{
width: 100%;
}
3.1.3 方式三:flex佈局-縱向+order排序實現
# 用 flexbox, :nth-child() 和 order 實現
缺點:需要自己寫容器的固定高度,不適用於小程式端
# wxml:
<view class='container'>
<view class="item" wx:for="{{dataList}}" wx:key="id">
<view>{{item.id}}</view>
<image src="{{item.img}}" mode="widthFix"></image>
</view>
</view>
# css:
/* 讓內容按列縱向展示*/
.container {
display: flex;
flex-flow: column wrap; /* 屬性是flex-direction和flex-wrap屬性的縮寫*/
align-content: space-between;
/* 容器必須有固定高度,不然全在第一列
* 且高度大於最高的列高 */
height: 1000rpx;
}
/* 重新定義內容塊排序優先順序,讓其橫向排序 */
.item:nth-child(2n+1) { order: 1; }
.item:nth-child(2n ) { order: 2; }
/* 強制使內容塊分列的隱藏列 */
.container::before,
.container::after {
content: "";
flex-basis: 100%;
width: 0;
order: 2;
}
.container .item{
width: 50%;
overflow: hidden;
}
.container .item image{
width: 100%;
}
3.2 View優化
優化目的:利用現成的類,來拓寫功能,提高程式碼的可重用性
3.2.1 使用過濾類--進行最大/最小的過濾
class ReachBottomFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
min_id = request.query_params.get('minId')
if not min_id:
return queryset
return queryset.filter(id__lt=min_id)
class PullDownRefreshFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
max_id = request.query_params.get('maxId')
if not max_id:
return queryset
return queryset.filter(id__gt=max_id).reverse() # 反向,或 order_by('id')
# 因為queryset 整體是倒序拍的,若下拉新資料(新資料100條)的大於10條,則應該是110-100,而不是200-190
# 加上 分頁切片都是從頭開始,則需要正向排序,此時返回資料 是100-110,前端再自己反向處理下
3.2.2 使用分頁類---來定製的資料展示條數
# 利用分頁類--偏移分頁,來定製的資料展示條數
# low版(自己版):
直接在過濾的時候,固定的切片
class PageFilter(BaseFilterBackend):
def filter_queryset(self, request, queryset, view):
max_id = request.query_params.get('maxId') # 獲取條件值
min_id = request.query_params.get('minId')
if max_id:
queryset = queryset.filter(id__gt=max_id).order_by('id')
if min_id:
queryset = queryset.filter(id__lt=min_id)
return queryset[:10] # 返回過濾完的資料
# nice版:
利用分頁類--偏移分頁的特性,limit: 取多少條,offset: 從第幾個位置開始取
這樣前端,可攜帶引數 ?limit=20 或不傳(default_limit),定製獲取條數
from rest_framework.pagination import LimitOffsetPagination
from rest_framework.response import Response
class OldBoyLimitPagination(LimitOffsetPagination):
"""
本質上幫助我們進行切片的處理:[0:N]
"""
default_limit = 5
max_limit = 50
limit_query_param = 'limit'
offset_query_param = 'offset'
# 重寫offset方法,固定成 每次都是第0個位置開始
def get_offset(self, request):
return 0
# 重寫get_paginated_response, 去掉預設攜帶的 count、next等引數
def get_paginated_response(self, data):
return Response(data)
3.3 擴充套件:分頁的優化
# 記錄最大值和最小值,進行過濾篩選
# 優點:
防止切片全部資料掃描的問題。
正常分頁時,是queryset[:10]的操作 (0-10、11-20)
取到第n頁資料時,也會將前n-1頁的資料一起查出來,再切片後10條資料
資料、分頁越多,效率越低
# 缺點:
只能上下頁進行翻頁,不能直接跳轉第n頁
4.詳細頁面(3點)
- 寫指令碼構造資料
- 最近的訪客
- 一級評論
4.1 寫指令碼構造資料
# 構造資料的方式:
1.前端(或postman)構造資料,傳送請求,存資料庫 # 正常執行邏輯
2.資料庫中,手動新增 或 sql指令碼
3.Django後端,寫orm指令碼 # 別忘了這個
# 使用 Django orm 單獨寫一個數據初始化的指令碼
import os
import sys
import django
# 將Django專案新增sys.path變數中
base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.append(base_dir)
# 將Django的配置 設定到 系統環境變數中
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "demos.settings")
django.setup()
from api import models
for i in range(1,37):
news_object = models.News.objects.create(
cover="https://mini-1251317460.cos.ap-chengdu.myqcloud.com/08a9daei1578736867828.png",
content="還有{0}天就放假".format(i),
topic_id=1,
user_id=1)
models.NewsDetail.objects.create(
key="08a9daei1578736867828.png",
cos_path="https://mini-1251317460.cos.ap-chengdu.myqcloud.com/08a9daei1578736867828.png",
news=news_object)
models.NewsDetail.objects.create(
key="0d3q0evq1578906084254.jpg",
cos_path="https://mini-1251317460.cos.ap-chengdu.myqcloud.com/0d3q0evq1578906084254.jpg",
news=news_object)
# 注意:
# 1.Django中,指令碼單獨執行,要使用 讀取Django的配置以及Django.setup()
# 2.sys.path 和 os.environ 的區別:
sys.path 是python的package path,是當前指令碼的載入目錄和模組的查詢目錄
append是臨時新增,如果退出當前會話,或者當前的shell,就會消失
os.environ 是一系列的鍵、值對儲存的字典,作業系統環境資訊全部儲存在該字典中
os.environ['HOMEPATH'] # 當前使用者主目錄
os.environ['TEMP'] # 臨時目錄路徑
os.environ['PATHEXT'] # 可執行檔案
os.environ['SYSTEMROOT'] # 系統主目錄
os.environ['LOGONSERVER'] # 機器名
os.environ['PROMPT'] # 設定提示符
os.environ['PATH'] # 系統環境資訊
小程式 暫時停止到這裡。完成功能:截止到動態詳細頁 (評論部分的api 和前端 未實現)
作業
-
贊文章
-
贊評論
-
關注
-
訪問記錄
進入詳細頁面時,先判斷使用者是否已經登入。 未登入,不操作。 登入:新增到訪問記錄中。