欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

电商项目的收货地址

程序员文章站 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就是修改,没有就是添加,上面有提到

电商项目的收货地址
电商项目的收货地址
编辑保存的后台代码在上面地址入库部分