电商项目的收货地址

收货地址主要分为:

1:添加地址
2:设置默认地址
3:编辑地址
4:删除地址
添加地址框里面涉及到了省市区三级联动问题
电商项目的收货地址_第1张图片
我们需要先创建一张自关联的城市表

# 城市表  自关联表
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
电商项目的收货地址_第2张图片

有数据就可以可以把数据渲染到页面上了 首先写一个点击事件,点击把增加地址框显示出来

// 获取一级城市
        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)
            })

在这里插入图片描述
电商项目的收货地址_第3张图片

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\d+)/',views.Get_edit_two.as_view()),
re_path(r'get_edit_three/(?P\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

现在三级连动和入库都做完了,该修改前端代码显示数据和设置默认地址了

将默认地址区和普通地址区分开做,这个默认地址如果有数据就会自动显示,没有就自动隐藏,这就好做了
进入页面先走一个钩子函数触动两个电机事件,分别获取默认地址的数据和普通地址数据,前端默认地址和普通地址区需要先默认变量

电商项目的收货地址_第4张图片
电商项目的收货地址_第5张图片

  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

电商项目的收货地址_第6张图片

// 设置默认地址
        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})

编辑地址

我们知道添加地址打开地址框是空白的,那么点击编辑也是点开的地址框,不同的是点击编辑地址显示的应该是含有当前数据的地址框

电商项目的收货地址_第7张图片

这个是首先触动一个点击事件携带着id向后台将当前地址数据获取到,那前端也是要先给一个默认的变量,将获取到的数据进行赋值,然后再让地址框显示

前端代码
电商项目的收货地址_第8张图片

  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\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就是修改,没有就是添加,上面有提到

电商项目的收货地址_第9张图片
电商项目的收货地址_第10张图片
编辑保存的后台代码在上面地址入库部分

你可能感兴趣的:(Django,vue)