Django_rest框架电商项目实践项目(一篇文章讲清楚电商项目)项目的创建与基本的配置,所有接口的代码,项目代码已给

2022-01-21 14:49:58 浏览数 (1)

目录

  • 需求
  • 创建一个Django项目
  • 配置基础东西
    • 连接mysql
    • 使用Django_rest
    • 解决跨域
    • 建模(model里面写实体类)
    • admin.py文件里面的创建
    • 手动在后台管理系统里面添加相关数据
  • 商品列表后台接口
    • 序列器
    • view代码
    • url代码
    • 实现分页(传limit offset)
    • 实现根据字段进行排序和模糊搜索
  • 商品分类查询
    • view代码
    • url代码
    • 前端调用的书写
    • 在分类查询的基础上面,需要排序
  • 根据生产厂商进行选择
    • view
    • url
    • 前段传参
  • 商品的详情
    • view
    • url
    • 前端如何传参
    • 查询的外键需要返回具体的name值,而不是id
      • 第一种方法
    • 第二种方法
  • 实现用户的登录
  • 用户的详情
    • 用户详情序列器
    • view
    • url
    • 前端如何做
  • 用户的注册
    • 序列化
    • view
    • url
  • 用户信息的更新
    • 序列器
    • view
    • url
  • 收货地址的新增和列表查询
    • 新增和list列表查询 (一个接口实现)
      • 序列器
      • view
      • url
    • 页面展示
  • 收货地址的回显,修改,删除(一个接口实现)
    • view
    • url
  • 购物车的查询新增
    • 新增购物车
      • 序列器
      • view
      • url
    • 购物车查询
      • 序列器
      • view
      • url
  • 订单
    • 订单列表展示
      • 序列器
      • view
      • url
    • 下单操作,删除操作
      • view
      • url

需求

做一个电商项目,就是卖不同品牌的电脑,可以根据不同的品牌进行查询,还有对订单的crud,还有用户的会员中心功能模块。还有收货地址模块

创建一个Django项目

用pycharm直接创建一个Django项目

配置基础东西

连接mysql

使用Django_rest

解决跨域

第一步

第二部

第三部

建模(model里面写实体类)

代码语言:javascript复制
from django.db import models

# Create your models here.
from django.db import models

# Create your models here.


from django.conf import settings



# @python_2_unicode_compatible
class Category(models.Model):
    """
    商品类别:笔记本、平板电脑、一体机、台式机、服务器
    """
    name = models.CharField(max_length=200)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name
    class Meta:
        verbose_name_plural = verbose_name = '商品类别'
        db_table = 'Category'



# @python_2_unicode_compatible
class Manufacturer(models.Model):
    """
    生产厂商
    """
    name = models.CharField(max_length=200)
    description = models.TextField()
    logo = models.ImageField(blank=True, null=True, max_length=200, upload_to='manufacturer/uploads/%Y/%m/%d/')
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.name
    class Meta:
        verbose_name_plural = verbose_name = '生产厂商'
        db_table = 'Manufacturer'



# @python_2_unicode_compatible
class Product(models.Model):
    """
    产品
    """
    model = models.CharField(max_length=200)
    description = models.TextField()
    image = models.ImageField(max_length=200, upload_to='product/uploads/%Y/%m/%d/')
    price = models.DecimalField(max_digits=12, decimal_places=2)
    sold = models.PositiveIntegerField(default=0)
    category = models.ForeignKey(Category, related_name='product_in', on_delete=models.CASCADE)
    manufacturer = models.ForeignKey(Manufacturer, related_name='product_of', on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.model
    class Meta:
        verbose_name_plural = verbose_name = '产品'
        db_table = 'Product'




# @python_2_unicode_compatible
class DeliveryAddress(models.Model):
    """
    收货地址
    """
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='delivery_address_of',)
    contact_person = models.CharField(max_length=200)
    contact_mobile_phone = models.CharField(max_length=200)
    delivery_address = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.delivery_address
    class Meta:
        verbose_name_plural = verbose_name = '收货地址'
        db_table = 'DeliveryAddress'





# @python_2_unicode_compatible
class UserProfile(models.Model):
    """
    用户档案
    """
    user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='profile_of',)
    mobile_phone = models.CharField(blank=True, null=True, max_length=200)
    nickname = models.CharField(blank=True, null=True, max_length=200)
    description = models.TextField(blank=True, null=True)
    icon = models.ImageField(blank=True, null=True, max_length=200, upload_to='user/uploads/%Y/%m/%d/')
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)
    delivery_address = models.ForeignKey(DeliveryAddress, related_name='user_delivery_address', on_delete=models.CASCADE, blank=True, null=True,)
    class Meta:
        verbose_name_plural = verbose_name = '用户档案'
        db_table = 'UserProfile'





# @python_2_unicode_compatible
class Order(models.Model):
    """
    Order
    """
    STATUS_CHOICES = (
        ('0', '最新订单'),
        ('1', '未支付'),
        ('2', '已支付'),
        ('3', '运输中'),
        ('4', '取消订单'),
    )
    status = models.CharField(choices=STATUS_CHOICES, default='0', max_length=2)
    user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='order_of',)
    remark = models.TextField(blank=True, null=True)
    product = models.ForeignKey(Product, related_name='order_product', on_delete=models.CASCADE)
    price = models.DecimalField(max_digits=12, decimal_places=2)
    quantity = models.PositiveIntegerField(default=1)
    address = models.ForeignKey(DeliveryAddress, related_name='order_address', on_delete=models.CASCADE)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    def __str__(self):
        return 'order of %d' % (self.user.id)

    class Meta:
        verbose_name_plural = verbose_name = '订单'
        db_table = 'Order'

admin.py文件里面的创建

这个是将我们的表注册到Django自带的后台里面

代码语言:javascript复制
from django.contrib import admin

# Register your models here.


from computerapp.models import Product, Category, Manufacturer, UserProfile, DeliveryAddress, Order

from django.contrib.auth.models import User



class UserProfileAdmin(admin.ModelAdmin):
    list_display = ['id', 'mobile_phone', 'nickname', 'user',]

admin.site.register(UserProfile, UserProfileAdmin)




class CategoryAdmin(admin.ModelAdmin):
    list_display = ['id', 'name',]

admin.site.register(Category, CategoryAdmin)


class ManufacturerAdmin(admin.ModelAdmin):
    list_display = ['id', 'name',]

admin.site.register(Manufacturer, ManufacturerAdmin)



class ProductAdmin(admin.ModelAdmin):
    list_display = ['id', 'model', 'price', 'category', 'manufacturer', 'sold',]
    list_editable = ['price', 'sold', 'category',]

admin.site.register(Product, ProductAdmin)




class DeliveryAddressAdmin(admin.ModelAdmin):
    list_display = ['id', 'user', 'contact_person', 'contact_mobile_phone', 'delivery_address',]

admin.site.register(DeliveryAddress, DeliveryAddressAdmin)




class OrderAdmin(admin.ModelAdmin):
    list_display = ['id', 'status', 'user',]

admin.site.register(Order, OrderAdmin)



# 全局配置
admin.site.site_header = '电商管理系统'
admin.site.site_title = '电商管理系统'

以上是Django自带的后台管理界面

手动在后台管理系统里面添加相关数据

商品列表后台接口

序列器

代码语言:javascript复制
# 产品列表序列器   展示的字段少
class ProductListSerializer(serializers.ModelSerializer):
    class Meta:
        model=Product
        fields=('id','model','image','price','sold','category','manufacturer',)

view代码

代码语言:javascript复制
class ProductListView(generics.ListAPIView):
    '''产品列表
    这个接口只有列表功能
    '''
    queryset=Product.objects.all()
    serializer_class=ProductListSerializer
    permissin_classes=(permissions.AllowAny,)   # 权限是任何人都可以调用这个接口
    filter_backends = (OrderingFilter,SearchFilter)
    ordering_fields=('category','manufacturer','created','price',)
    search_fields=('description','model')
    # ordering=('id',)
    pagination_class = LimitOffsetPagination

url代码

代码语言:javascript复制
  url(r'^product_list/$',views.ProductListView.as_view(),name='product_list'),

实现分页(传limit offset)

代码语言:javascript复制
REST_FRAMEWORK = {
# 新版本必须写下面的话
    'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 2,
}

以上是默认的每一页有3个数据

也就是前端调用后端的这个接口,默认给返回3条数据,使用的接口是

代码语言:javascript复制
http://127.0.0.1:8000/computer/product_list/

那么既然项目可以使用分页,前端如何使用分页功能呢?也就是想要第二页,第三页的数据,我们如何使用呢?

代码语言:javascript复制
http://127.0.0.1:8000/computer/product_list/?offset=6

前端如何限制每一页有几条数据,可以使用limit 前提是view里面要写这个

代码语言:javascript复制
    pagination_class = LimitOffsetPagination

以上就可以实现,对于列表展示的分页功能

实现根据字段进行排序和模糊搜索

以上可以实现对列表的查询,并且实现了分页,但是现在我们想要根据某一个字段进行排序和模糊搜索,我们可以在对应的接口里面的view代码里面这样配置

代码语言:javascript复制
  filter_backends = (OrderingFilter,SearchFilter)
    ordering_fields=('category','manufacturer','created','sold',)
    search_fields=('description','model')

这样写了之后,前端调用这个接口,需要根据某一个字段进行排序,可以这样传

代码语言:javascript复制
http://127.0.0.1:8000/computer/product_list/?ordering=created
代码语言:javascript复制
http://127.0.0.1:8000/computer/product_list/?search=华为手机01

商品分类查询

因为商品表里面有一个外键,就是产品的分类

view代码

代码语言:javascript复制
class ProductListByCategoryView(generics.ListAPIView):
    '''产品类别列表'''
    serializer_class=ProductListSerializer
    permissin_classes=(permissions.AllowAny,)
    filter_backends = (OrderingFilter,SearchFilter)
    ordering_fields=('category','manufacturer','created','sold','stock','price',)
    search_fields=('description',)
    ordering=('id',)

    def get_queryset(self):  # 这个就是rest框架的方法,不能变,就是定义查询的范围的,获取查询级
        category=self.request.query_params.get('category',None)  # 获取前段传过来的这个字段

        if category is not None:
            queryset = Product.objects.filter(category=category)  # 根据产品分类进行筛选查询
        else:
            queryset=Product.objects.all()

        return queryset

url代码

代码语言:javascript复制
 url(r'^product_list_by_category/$',views.ProductListByCategoryView.as_view(),name='productlistbycategory'),

前端调用的书写

代码语言:javascript复制
http://127.0.0.1:8000/computer/product_list_by_category/?category=6

product_list_by_category就是我们第二个接口的名字,category是分类字段,当前数据库中有2个分类

category 根据这个字段,查询对应分类的产品

在分类查询的基础上面,需要排序

比如根据价格排序

根据生产厂商进行选择

view

代码语言:javascript复制
class ProductListByCategoryManufacturerView(generics.ListAPIView):
    '''产品按类别品牌列表'''
    serializer_class=ProductListSerializer
    permissin_classes=(permissions.AllowAny,)
    filter_backends = (OrderingFilter,SearchFilter)
    ordering_fields=('category','manufacturer','created','sold','stock','price',)
    search_fields=('description',)
    ordering=('id',)

    def get_queryset(self):
        category=self.request.query_params.get('category',None)
        manufacturer = self.request.query_params.get('manufacturer', None)

        if category is not None:
            queryset = Product.objects.filter(category=category,manufacturer=manufacturer)
        else:
            queryset=Product.objects.all()

        return queryset

url

代码语言:javascript复制
url(r'^product_list_by_category_manufacturer/$',views.ProductListByCategoryManufacturerView.as_view(),name='product_list_by_category_manufacturer'),

前段传参

商品的详情

也就是根据列表数据的id值,进行查询数据库,将单个数据的详情进行返回给前端

view

代码语言:javascript复制
class ProductRetrieveView(generics.RetrieveAPIView):  # 继承详情的类
    '''产品详情'''
    queryset = Product.objects.all()
    serializer_class = ProductRetrieveSerializer
    permission_classes = (permissions.AllowAny,)

url

代码语言:javascript复制
url(r'^product_retrieve/(?P<pk>[0-9] )/$', views.ProductRetrieveView.as_view(),name='product_retrieve'),

参数是pk 他的值就是数据的id,路径的这个接参属性必须是pk

前端如何传参

查询的外键需要返回具体的name值,而不是id

第一种方法

代码语言:javascript复制
class ProductRetrieveSerializer(serializers.ModelSerializer):
    # manufacturer = ManufacturerSerializer
    # category = CategorySerializer
    category_name = serializers.CharField(source='category.name')
    manufacturer_name = serializers.CharField(source='manufacturer.name')
    class Meta:
        model = Product
        fields = ('id','model','image','price','sold','manufacturer_name','category_name','description','created','updated',)

第二种方法

序列器必须加括号,相当于创建对象

实现用户的登录

首先在setting里面进行配置,使用token进行登录 这个是rest框架自带

配置完成,进行生成数据库迁移 数据库里面会生成

就会多一个这个,配置文件里面,多加这个

代码语言:javascript复制
REST_FRAMEWORK = {
# 新版本必须写下面的话
    'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
    'PAGE_SIZE': 3,
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        主要是这个
        'rest_framework.authentication.TokenAuthentication',
    ),
}

在跟url里面写

代码语言:javascript复制
    url('^api-token-auth/',views.obtain_auth_token),

前段调用 http://127.0.0.1:8000/api-token-auth/,这个将用户名密码传过去,就可以实现token登录

用户的详情

用户详情序列器

代码语言:javascript复制
class UserInfoSerializer(serializers.ModelSerializer):
    profile_of=UserProfileSerializer()
    class Meta:
        model=User
        fields=('id','username','email','first_name','last_name','date_joined','profile_of',)

view

代码语言:javascript复制
class UserInfoView(APIView):
    '''用户基本信息'''
    permission_classes=(permissions.IsAuthenticated,)
    def get(self,request,format=None):
        user=self.request.user
        serializer=UserInfoSerializer(user)
        return Response(serializer.data)

url

代码语言:javascript复制
 url(r'^user_info/$',views.UserInfoView.as_view(),name='user_info'),

前端如何做

用户登录成功,就跳转到用户详情页面,在已进入这个页面,那就调用用户详情的接口,在这个接口的view里面进行权限限制

如果成功,就回显数据,如果失败,那么就跳转到登录页面

用户的注册

往用户表和会员档案里面保存数据

序列化

代码语言:javascript复制
# 创建用户的序列器
class UserSerializer(serializers.ModelSerializer):
    class Meta:
        model=User
        # 用户注册需要的字段
        fields=('id','username','password','email','first_name','last_name',)
        # 创建完成的字段,这个里面设置的就是不会把这个字段返回给前端
        extra_kwargs={'password':{'write_only':True}}

    # 重写父类的创建方法
    def create(self, validated_data):  # 创建的时候  自动执行这个方法
        user=User(**validated_data)#接受前端传过来的用户名和密码
        user.set_password(validated_data['password'])#通过字典方式调用,进行密码加密
        user.save()#保存到内存中
        user_profile=UserProfile(user=user)   # 创建会员档案
        user_profile.save()  # 会员档案的保存
        return user

view

代码语言:javascript复制
class UserCreateView(generics.CreateAPIView):
    '''用户创建'''
    serializer_class = UserSerializer

url

代码语言:javascript复制
   url(r'^user_create/$', views.UserCreateView.as_view(),
        name='user_create'),

用户信息的更新

序列器

代码语言:javascript复制
class UserProfileSerializer(serializers.ModelSerializer):
    class Meta:
        model=UserProfile
        fields=('id','user','mobile_phone','nickname','description','icon','created','updated',)
        read_only_fields=('user',)

view

代码语言:javascript复制
# 更新
class UserProfileRUView(generics.RetrieveUpdateAPIView):
    '''用户其他信息'''
    serializer_class = UserProfileSerializer
    permission_classes = (permissions.IsAuthenticated,)

    def get_object(self):
        user = self.request.user
        obj =UserProfile.objects.get(user=user)
        return obj

url

代码语言:javascript复制
 url(r'^user_profile_ru/(?P<pk>[0-9] )/$', views.UserProfileRUView.as_view(),name='user_profile_ru'),

收货地址的新增和列表查询

新增和list列表查询 (一个接口实现)

新增收货地址,不仅仅要在地址表里面新增,而且还要将用户档案里面的地址变为当前最新新增的。

传了参数,并且是put请求 ,就是新增 不传,直接调用,get请求,就是列表查询

序列器

代码语言:javascript复制
class DeliveryAddressSerilizer(serializers.ModelSerializer):
    '''收货地址'''
    class Meta:
        model=DeliveryAddress
        fields=('id','user','contact_person','contact_mobile_phone','delivery_address','created','updated',)
             # 这个字段只是列表查询的时候用
        read_only_fields = ('user',)

view

代码语言:javascript复制
class DeliveryAddressLCView(generics.ListCreateAPIView):# ListCreateAPIView  继承这个,既可以创建,也可以list
    '''收货地址LC'''
    serializer_class = DeliveryAddressSerilizer
    permission_classes = (permissions.IsAuthenticated,)
    def get_queryset(self):  # 重写源码,获取查询级,也就是获取当前用户的地址
        user = self.request.user
        queryset= DeliveryAddress.objects.filter(user=user)
        return queryset

    def perform_create(self, serializer):  # 重写源码里面的创建方法,不仅要地址表新增,还要更新会员表
        # serializer 这个参数的意思是    当前对象的序列器
        user=self.request.user
        s=serializer.save(user=user) # 根据序列器直接保存数据
        # 设置用户的默认地址
        profile=user.profile_of
        profile.delivery_address=s
        profile.save()

url

代码语言:javascript复制
url(r'^delivery_address_lc/$',views.DeliveryAddressLCView.as_view(),name='delivery_address_lc'),

页面展示

收货地址的回显,修改,删除(一个接口实现)

传的method不一样

view

代码语言:javascript复制
class DeliveryAddressRUDView(generics.RetrieveUpdateDestroyAPIView):
    '''收货地址RUD'''
    serializer_class = DeliveryAddressSerilizer
    permission_classes = (permissions.IsAuthenticated,)

    # 只能使用当前用户的,限制了用户
    def get_object(self):
        user = self.request.user
        # obj =DeliveryAddress.objects.get(user=user)
        try:
            obj=DeliveryAddress.objects.get(id=self.kwargs['pk'],user=user)
        except Exception as e:
            raise NotFound('no found')
        return obj

url

代码语言:javascript复制
url(r'^delivery_address_rud/(?P<pk>[0-9] )/$', views.DeliveryAddressRUDView.as_view(),name='delivery_address_rud'),

购物车的查询新增

购物车是一张表,这个表和订单表是同一个表,只是状态是不一样的。这个表有一个外键和用户关联。

新增购物车

序列器

代码语言:javascript复制
class OrderCreateSerializer(serializers.ModelSerializer):
    class Meta:
        model = Order
        fields = ('id', 'status', 'user', 'product', 'price', 'quantity', 'remark', 'address', 'created', 'updated',)
        read_only_fields=('user','price','address',)

view

代码语言:javascript复制
class OrderCreateView(generics.CreateAPIView):
    '''加入购物车'''
    queryset = Order.objects.all()
    serializer_class = OrderCreateSerializer
    permission_classes = (permissions.IsAuthenticated,)

    def perform_create(self, serializer):
        user = self.request.user
        product=serializer.validated_data.get('product')
        serializer.save(user=user,price=product.price,address=user.profile_of.delivery_address,status='0',)

        logging.info('user %d cart changed,product %d related.Time is %s.', user.id, product.id, str(datetime.datetime.now()))

url

代码语言:javascript复制
    url(r'^order_create/$',views.OrderCreateView.as_view(),name='order_create'),

购物车查询

序列器

代码语言:javascript复制
class OrderListSerializer(serializers.ModelSerializer):
    product = ProductListSerializer()
    address = DeliveryAddressSerilizer()
    class Meta:
        model=Order
        fields=('id','status','user','product','price','quantity','remark','address','created','updated',)

view

代码语言:javascript复制
class CartListView(generics.ListAPIView):
    '''购物车列表'''
    serializer_class=OrderListSerializer
    permissin_classes=(permissions.IsAuthenticated,)

    def get_queryset(self):  # 获取特定的结果集  筛选
       user=self.request.user
       queryset=Order.objects.filter(user=user,status='0')
       return queryset

url

代码语言:javascript复制
    url(r'^cart_list/$',views.CartListView.as_view(),name='cart_list'),

订单

和购物车同一张表,只是状态不一样

订单列表展示

序列器

代码语言:javascript复制
class OrderListSerializer(serializers.ModelSerializer):
    product = ProductListSerializer()
    address = DeliveryAddressSerilizer()
    class Meta:
        model=Order
        fields=('id','status','user','product','price','quantity','remark','address','created','updated',)

view

代码语言:javascript复制
class OrderListView(generics.ListAPIView):
    '''订单列表'''
    serializer_class=OrderListSerializer
    permissin_classes=(permissions.IsAuthenticated,)

    def get_queryset(self):
       user=self.request.user
       queryset=Order.objects.filter(user=user,status__in=['1','2','3','4'])
       return queryset

url

代码语言:javascript复制
    url(r'^order_list/$',views.OrderListView.as_view(),name='order_list'),

下单操作,删除操作

view

代码语言:javascript复制
class OrderRUDView(generics.RetrieveUpdateDestroyAPIView):
    '''Order  更新  回显   删除  '''
    serializer_class = OrderRUDSerializer
    permission_classes = (permissions.IsAuthenticated,)

    def get_object(self):
        user = self.request.user
        obj= Order.objects.get(user=user,id=self.kwargs['pk'])
        return obj

    # 更新 下单的时候走这个
    def perform_update(self, serializer):
        user = self.request.user
        print('oooooooo')
        serializer.save(user=user,status='1')

url

代码语言:javascript复制
    # 回显,更新,删除
    url(r'^order_rud/(?P<pk>[0-9] )/$', views.OrderRUDView.as_view(), name='order_update'),

0 人点赞