电商项目的收货地址
程序员文章站
2022-04-14 21:09:47
...
收货地址主要分为:
1:添加地址
2:设置默认地址
3:编辑地址
4:删除地址
添加地址框里面涉及到了省市区三级联动问题
我们需要先创建一张自关联的城市表
# 城市表 自关联表
class City(models.Model):
name = models.CharField(max_length=20,verbose_name='城市名字')
city_id = models.IntegerField(verbose_name='城市ID')
parent = models.ForeignKey(
to = 'self', # 自己关联自己
on_delete = models.SET_NULL,
null = True, # 允许字段为 None值
blank = True, # 输入框可以不输入
related_name = 'subs' # 反向查询
)
def __str__(self):
return self.name
class Meta:
db_table = 'city'
verbose_name_plural = '城市'
创好表后我手动输入了一点假数据,右边关联左边ID
有数据就可以可以把数据渲染到页面上了 首先写一个点击事件,点击把增加地址框显示出来
// 获取一级城市
add_edit:function(){
this.is_show_edit = true
this.axios({
url:"http://127.0.0.1:8000/api/get_edit_one/",
method:'get'
}).then(res=>{
this.city_one = res.data # 将获取到的省数据复制到原本的默认省变量上
this.get_two_city()
}).catch(error=>{
console.log(error)
})
select 下拉框有个
@change属性,可以获取选中的value,我们v-model绑定了省的ID,当选中哪个省的同事就获取到了当前省ID和触动了一个点击方法
二级城市三级城市同理
// 获取二级城市
get_two_city:function(){
this.axios({
url:"http://127.0.0.1:8000/api/get_edit_two/" + this.one_city_id + '/',
method:'get'
}).then(res=>{
this.city_two = res.data
}).catch(error=>{
console.log(error)
})
},
// 获取三级城市
get_three_city:function(){
this.axios({
url:"http://127.0.0.1:8000/api/get_edit_three/" + this.two_city_id + '/',
method:'get'
}).then(res=>{
this.city_three = res.data
}).catch(error=>{
console.log(error)
})
},
三级联动后台代码
# 正则路由传参
re_path(r'get_edit_two/(?P<one_city_id>\d+)/',views.Get_edit_two.as_view()),
re_path(r'get_edit_three/(?P<two_city_id>\d+)/',views.Get_edit_three.as_view()),
from rest_framework import generics
# 获取一级城市
class Get_edit_one(generics.ListAPIView):
'''
条件查询数据库 parent=None的城市代表是最上级,
用 generics.ListAPIView 类视图可以直接调用它的序列化器,自动返回
'''
# queryset:底层的方法
queryset = models.City.objects.filter(parent=None).all()
# 查询出来的数据对象 queryset 会自动走下面这个序列化器并返回
serializer_class = serializer.CitySerializer
# 获取二级城市数据
class Get_edit_two(generics.ListAPIView):
# 使用 generics.ListAPIView 会将数据序列化并返回
serializer_class = serializer.CitySerializer
def get_queryset(self):
# self.kwargs:前端发过来的数据
one_city_id = self.kwargs['one_city_id']
city_two_mes = models.City.objects.filter(parent__city_id=one_city_id).all()
# 将查询出的数据对象 return 到的 get_queryset 方法会自动去找 序列化器并返回
return city_two_mes
# 获取三级城市数据
class Get_edit_three(generics.ListAPIView):
serializer_class = serializer.CitySerializer
def get_queryset(self):
two_city_id = self.kwargs['two_city_id']
city_three_mes = models.City.objects.filter(parent__city_id=two_city_id).all()
return city_three_mes
三级联动的序列化器如下
# 城市表序列化器
class CitySerializer(serializers.ModelSerializer):
class Meta:
model = models.City
fields = ('city_id','name')
提交地址数据
//提交地址数据
sub_add:function(adres_id){
var zipFormData = new FormData();
zipFormData.append('receiver',this.receiver);
zipFormData.append('province',this.one_city_id);
zipFormData.append('city',this.two_city_id);
zipFormData.append('town',this.three_city_id);
zipFormData.append('place',this.place);
zipFormData.append('mobile',this.mobile);
zipFormData.append('email',this.email);
zipFormData.append('adres_id',adres_id);
this.axios({
url:"http://127.0.0.1:8000/api/inert_addres/",
method:'post',
data:zipFormData,
headers:{
'Authorization': 'JWT ' + localStorage.token
}
}).then(res=>{
console.log(res)
if(res.data.code==200){
this.is_show_edit = false
this.reload();
}else{
this.city_show = true
this.city_mes = res.data.mes
}
}).catch(error=>{
console.log(error)
})
},
后台代码
首先要创建一个地址表,要关联城市表和用户表
# 时间表
class BaseModel(models.Model):
create_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now_add=True)
class Meta():
abstract = True
# 地址表
class Address(BaseModel,models.Model):
user = models.ForeignKey(Users,on_delete=models.CASCADE,related_name='addres',verbose_name='用户')
receiver = models.CharField(max_length=20,verbose_name='收件人')
# models.PROTECT:删除关联数据,引发错误ProtectedError
province = models.ForeignKey(City,on_delete=models.PROTECT,related_name='province_addres',verbose_name='省')
city = models.ForeignKey(City,on_delete=models.PROTECT,related_name='city_addres',verbose_name='市')
town = models.ForeignKey(City,on_delete=models.PROTECT,related_name='town_addres',verbose_name='区')
place = models.CharField(max_length=50,verbose_name='地址')
mobile = models.CharField(max_length=11,verbose_name='手机号')
email = models.CharField(max_length=30,null=True,blank=True,default="",verbose_name='邮箱')
is_delete = models.BooleanField(default=False,verbose_name='逻辑删除')
default_address = models.IntegerField(default=0,verbose_name='默认地址')
class Meta:
db_table = 'address'
verbose_name = '用户地址表'
ordering = ['-update_time'] # 默认的排序方式
地址数据入库
# 地址入库
class Inert_addres(APIView):
permission_classes = (IsAuthenticated,)
def post(self,request):
user = request.user
province_id = request.data['province']
city_id = request.data['city']
town_id = request.data['town']
adres_id = request.data['adres_id'] # 获取前端传的ID,有ID就是修改,没有就是添加
if not all([province_id,city_id,town_id]):
return Response({'code':201,'mes':'选项不能为空'})
province = models.City.objects.filter(city_id=province_id).first()
city = models.City.objects.filter(city_id=city_id).first()
town = models.City.objects.filter(city_id=town_id).first()
# 有 id 就是修改 这一块是涉及后面的编辑地址的
# 点击编辑也是打开的地址框,但编辑时地址框是应该有数据的,所以这里用前端是否有传ID判断 是添加还是编辑
if adres_id:
receiver = request.data['receiver']
place = request.data['place']
mobile = request.data['mobile']
email = request.data['email']
models.Address.objects.filter(id=adres_id).update(
receiver = receiver,place = place,mobile = mobile,email = email,
province = province,city = city,town = town
)
return Response({'code':200})
else:
# 将 数据对象传参到序列化器
address = serializer.addressSerializer(data=request.data,context={'user':user,'province':province,'city':city,'town':town})
if address.is_valid():
address.save()
return Response({'code':200})
else:
return Response({'code':201,'mes':address.errors})
地址表入库序列化器
# 地址表序列化器
class addressSerializer(serializers.ModelSerializer):
# 外键字段设为可读字段 是为了序列化查询用的
# PrimaryKeyRelatedField:只显示外键关联对象的主键ID
user = serializers.PrimaryKeyRelatedField(read_only=True)
# 指定反序列化入库的外键
province = serializers.IntegerField(label='省ID', write_only= True)
city = serializers.IntegerField(label='市ID', write_only= True)
town = serializers.IntegerField(label='区ID', write_only= True)
# 序列化输出 外键入库的是ID,但我需要查询出来的是 名字,这里定义外键输出类型
province = serializers.StringRelatedField(read_only=True)
city = serializers.StringRelatedField(read_only=True)
town = serializers.StringRelatedField(read_only=True)
class Meta:
fields = "__all__"
model = models.Address # 指定的表
def create(self,validated_data):
# 反序列化入库外键对象要传参过来 user:表外键字段
users = models.Address.objects.create(
user=self.context['user'],
province=self.context['province'],
city=self.context['city'],
town=self.context['town'],
**validated_data
) # 创建用户
return users
现在三级连动和入库都做完了,该修改前端代码显示数据和设置默认地址了
将默认地址区和普通地址区分开做,这个默认地址如果有数据就会自动显示,没有就自动隐藏,这就好做了
进入页面先走一个钩子函数触动两个电机事件,分别获取默认地址的数据和普通地址数据,前端默认地址和普通地址区需要先默认变量
data:function(){
return{
address_list:[], // 地址数据
len_address:"", // 总地址数
default_list:[], // 默认地址
}
},
触动点击事件获取数据
mounted() {
// 获取地址
this.get_address()
// 获取默认地址
this.get_def_address()
},
// 获取地址
get_address:function(){
this.axios({
url:'http://127.0.0.1:8000/api/get_address_mes/',
method:'get',
headers:{
'Authorization':'JWT ' + localStorage.token
}
}).then(res=>{
# 将获取的地址总数赋值
this.len_address = res.data.len_address
# 给默认地址变量赋值
this.address_list = res.data.address
}).catch(error=>{
console.log(error)
this.$router.push({'path':'/login'})
})
},
// 获取默认地址
get_def_address:function(){
this.axios({
url:'http://127.0.0.1:8000/api/def_address_mes/',
method:'get',
headers:{
'Authorization':'JWT ' + localStorage.token
}
}).then(res=>{
if(res.data.code==200){
this.default_list = res.data.mes
}
}).catch(error=>{
console.log(error)
this.$router.push({'path':'/login'})
})
},
后台代码
# 获取用户地址
class Get_address_mes(APIView):
permission_classes = (IsAuthenticated,)
def get(self,request):
user = request.user
address = models.Address.objects.filter(user=user,is_delete=False,default_address=0).all()
address_all = models.Address.objects.filter(user=user,is_delete=False).all()
len_address = len(address_all)
user_address = serializer.addressSerializer(instance=address,many=True)
return Response({
'address':user_address.data,
'len_address':len_address
})
# 获取默认地址
class Def_address_mes(APIView):
permission_classes = (IsAuthenticated,)
def get(self,request):
user = request.user
address = models.Address.objects.filter(user=user,is_delete=False,default_address=1).all()
if address:
def_address = serializer.addressSerializer(instance=address,many=True)
return Response(
{'code':200,'mes':def_address.data}
)
else:
return Response(
{'code':201}
)
设置默认地址
点击当前地址设为默认要带着当前地址ID过去,后台拿到ID将当前地址的 is_delete(是否默认)字段设为1(1是默认,0是不默认),其他地址设为0
// 设置默认地址
SetDefault:function(adres_id){
this.axios({
url:'http://127.0.0.1:8000/api/set_def_address/' + adres_id + '/',
method:'get',
headers:{
'Authorization':'JWT ' + localStorage.token
}
}).then(res=>{
if(res.data.code==200){
this.reload();
}
}).catch(error=>{
console.log(error)
this.$router.push({'path':'/login'})
})
},
后台代码
from django.db.models import Q # ~Q:不等于
# 设置默认地址
class Set_def_address(APIView):
permission_classes = (IsAuthenticated,)
# adres_id:正则路由传参
def get(self,request,adres_id):
# 将当前ID的地址设为默认
models.Address.objects.filter(id=adres_id).update(default_address=1)
# 将别的地址设为 不默认
models.Address.objects.filter(~Q(id=adres_id)).update(default_address=0)
return Response({'code':200})
编辑地址
我们知道添加地址打开地址框是空白的,那么点击编辑也是点开的地址框,不同的是点击编辑地址显示的应该是含有当前数据的地址框
这个是首先触动一个点击事件携带着id向后台将当前地址数据获取到,那前端也是要先给一个默认的变量,将获取到的数据进行赋值,然后再让地址框显示
前端代码
data:function(){
return{
receiver:"", // 收件人
place:"", // 地址
mobile:"", // 手机号
email:"", // 邮箱
red_id:"", //当前地址ID 点击编辑保存时要用这个ID做条件后台更新数据
redact_addres_id:false, // 当前地址ID input框
}
},
// 编辑地址
redact_address:function(adres_id){
this.axios({
url:'http://127.0.0.1:8000/api/redact_address/' + adres_id + '/',
method:'get',
headers:{
'Authorization':'JWT ' + localStorage.token
}
}).then(res=>{
if(res.data.code==200){
console.log(res.data)
this.receiver = res.data.mes.receiver
this.place = res.data.mes.place
this.mobile = res.data.mes.mobile
this.email = res.data.mes.email
# 给当前地址 ID 赋值
this.red_id = res.data.mes.id
# 将当前地址数据获取到后触动三级连动事件
this.add_edit()
}
}).catch(error=>{
console.log(error)
// this.$router.push({'path':'/login'})
})
}
后台代码
re_path(r'redact_address/(?P<adres_id>\d+)/',views.Redact_address.as_view()),
# 编辑地址
class Redact_address(APIView):
permission_classes = (IsAuthenticated,)
def get(self,request,adres_id):
address = models.Address.objects.filter(id=adres_id).first()
if address:
red_address = serializer.addressSerializer(instance=address)
return Response({'code':200,'mes':red_address.data})
else:
return Response({'code':201})
现在点击编辑,地址数据就能渲染到地址框了,修改完点击保存,会传一个ID 访问后台添加地址接口,有这个ID就是修改,没有就是添加,上面有提到
编辑保存的后台代码在上面地址入库部分