路漫漫其修远兮
吾将上下而求索

drf学习:Vue+Django REST framework打造生鲜电商项目1-5章

课程:Vue+Django REST framework打造生鲜电商项目
地址:https://coding.imooc.com/class/131.html
代码:https://git.andblog.cn/root/py-mxshop
https://github.com/derek-zhang123/MxShop

下面只是列出了一些重要的步骤,其他的在博客上面查看知识点
http://www.cnblogs.com/derek1184405959/p/8733194.html


看完了drf基础
感觉还是要看mxshop这个项目
快餐,开始根据视频做自己的项目
理解了基础,这个再看就明白很多了
还是要多用,常用,越看越熟练

这里写每节做的事情,包括代码,gitlab里面同步将代码写好,提交名字写好每节的名字,方便以后查阅





第1章 课程介绍
介绍课程目标、通过课程能学习到的内容、和系统开发前需要具备的知识


1-1 课程导学
第2章 开发环境搭建
介绍系统开发所需的开发环境的搭建, 包括前后端开发所需要的IDE、 mysql、navicat、nodejs、cnpm的配置等, 还介绍了如何配置python虚拟环境


2-1 pycharm的安装和简单使用
2-2 mysql和navicat的安装和使用_1
2-3 windows和linux下安装python2和python3_1
2-4 虚拟环境的安装和配置 
具体查看在线教学平台的笔记


2-5 vue开发环境搭建 
用webstorm打开,然后安装依赖,前端就可以正常运行了


2-6 资源获取方式和提问方式 






第3章 model设计和资源导入
本章节分为项目初始化、model设计和数据导入三部分。 章节详细的讲解了如何初始化项目、分析业务需求之后设计django的app、然后设计好各个app的model,
然后介绍如何配置已经写好的xadmin后台管理系统、以及如何去单独使用django的model去从原始文件导入初始化商品数据。...

3-1 项目初始化


workon mxshop
pip install djangorestframework
pip install django==1.11.6
pip install markdown
pip install django-filter
pip install pillow
pip install pymysql

然后通过pycharm来创建项目,执行文件选择虚拟环境中的执行文件
如果通过命令行创建:django-admin startproject <your-project>

将user目录移动到app目录里面

需要创建数据库mxshop,编码:utf8-unicode,字符集:utf8-generate-cli
grant all on *.* to 'root'@'192.168.170.30' identified by '12345';

安装mysqlclient客户端,使用特定的包进行安装,具体看博客

在settings中添加path路径
sys.path.insert(0,BASE_DIR)
sys.path.insert(0,os.path.join(BASE_DIR, 'apps'))
sys.path.insert(0,os.path.join(BASE_DIR, 'extra_apps'))



3-2 user models设计
python manage.py startapp goods
python manage.py startapp trade
python manage.py startapp user_operation


datetime.now()系统编译时间
datetime.now当前时间

users里面定义了 userprofile,但是django不知道这个自定义的userprofile存在,需要告诉django新定义的存在

#settings.py
#重载系统的用户,让UserProfile生效
AUTH_USER_MODEL = 'users.UserProfile'

这里的用户类继承AbstractUser,也就是说包含django自带的字段,只是在这个类中添加以下用户自定义的需要的额外的字段
class UserProfile(AbstractUser):
    GENDER_CHOICES = (
        ('male', u'男'),
        ('female', u'女')
    )
    name = models.CharField('姓名', max_length=30, null=True, blank=True)  #null=True, blank=True表示为可以为null
    birthday = models.DateField('出生年月', null=True, blank=True)
    gender = models.CharField('性别', max_length=6, choices=GENDER_CHOICES, default='female')
    mobile = models.CharField('电话', max_length=11)
    email = models.EmailField('邮箱', max_length=100, null=True, blank=True)

    class Meta:
        verbose_name = '用户信息'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.username


class VerifyCode(models.Model):
    code = models.CharField('验证码', max_length=10)
    mobile = models.CharField('电话', max_length=11)
    add_time = models.DateTimeField('添加时间', default=datetime.now)

    class Meta:
        verbose_name = '短信验证'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.code


数据库进行生成处理,执行完成后,在数据库就可以看到更改了
python manage.py makemigrations
python manage.py migrate





3-3 goods的model设计


is_tab 指的是是否将此商品分类放到行向的导航栏里面
help_text 是后面api文档里面要用到这个说明

每个商品类别,不管是哪一级的类,都是相同的表字段,然后每个商品类别都有一个父级别,是一个外键,连接到自己
不多见,思路值得学习

class GoodsCategory(models.Model):
    """
    商品分类
    """
    CATEGORY_TYPE = (
        (1, '一级类目'),
        (2, '二级类目'),
        (3, '三级类目'),
    )

    name = models.CharField('类别名', default='', max_length=30, help_text='类别名')
    code = models.CharField('类别code', default='', max_length=30, help_text='类别code')
    desc = models.TextField('类别描述', default='', help_text='类别描述')

    category_type = models.IntegerField('类目级别', choices=CATEGORY_TYPE, help_text='类目级别')
    parent_category = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, verbose_name="父类目级别", help_text="父目录",
                                        related_name="sub_cat")
    is_tab = models.BooleanField('是否导航', default=False, help_text='是否导航')
    add_time = models.DateTimeField('添加时间', default=datetime.now)

    class Meta:
        verbose_name = '商品类别'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.name



class GoodsCategoryBrand(models.Model):
    """
    某一大类下的宣传商标
    """
    category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE, related_name='brands', null=True,
                                 blank=True, verbose_name='商品类目')
    name = models.CharField('品牌名', default='', max_length=30, help_text='品牌名')
    desc = models.TextField('品牌描述', default='', max_length=200, help_text='品牌描述')
    image = models.ImageField(max_length=200, upload_to='brands')    #是在media目录下面的子目录 media/brands
    add_time = models.DateTimeField('添加时间', default=datetime.now)

    class Meta:
        verbose_name = '宣传视频'
        verbose_name_plural = verbose_name
        db_table = 'goods_goodsbrand'

    def __str__(self):
        return self.name






加入django ueditor app
goods_desc = UEditorField  这个字段使用ueditor插件里面的,从extraapp里面拷贝,和github上有可能不同,使用老师的,app要添加到install_app里面
富文本编辑器里面有可能上传图片,有可能上传文件


class Goods(models.Model):
    """
    商品
    """
    category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE, verbose_name='商品类目')
    goods_sn = models.CharField('商品唯一货号', max_length=50, default='')
    name = models.CharField('商品名', max_length=100)
    click_num = models.IntegerField('点击数', default=0)
    sold_num = models.IntegerField('商品销售量', default=0)
    fav_num = models.IntegerField('收藏数', default=0)
    goods_num = models.IntegerField('库存数', default=0)
    market_price = models.FloatField('市场价格', default=0)
    shop_price = models.FloatField('本店价格', default=0)
    goods_brief = models.TextField('商品简短描述', max_length=500)
    goods_desc = UEditorField(verbose_name=u'内容', imagePath='goods/images/', width=1000, height=300,
                              filePath="goods/files/", default='')
    ship_free = models.BooleanField('是否承担运费', default=True)
    goods_front_image = models.ImageField(upload_to='goods/images', null=True, blank=True, verbose_name='封面图')
    is_new = models.BooleanField('是否新品', default=False)
    is_hot = models.BooleanField('是否热销', default=False)
    add_time = models.DateTimeField('添加时间', default=datetime.now)

    class Meta:
        verbose_name = '商品信息'
        verbose_name_plural = verbose_name



商品详情里面的轮播图片
class GoodsImage(models.Model):
    """
    商品轮播图
    """
    goods = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name='商品', related_name='images')
    image = models.ImageField(upload_to='', verbose_name='图片', null=True, blank=True)
    add_time = models.DateTimeField('添加时间', default=datetime.now)

    class Meta:
        verbose_name = '商品轮播'
        verbose_name_plural = verbose_name


class Banner(models.Model):
    """
    首页轮播商品
    """
    goods = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name='商品')
    image = models.ImageField(upload_to='banner', verbose_name='轮播图片')
    index = models.IntegerField('轮播顺序', default=0)
    add_time = models.DateTimeField('添加时间', default=datetime.now)

    class Meta:
        verbose_name = '首页轮播'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.goods.name


class IndexAd(models.Model):
    """
    商品广告
    """
    category = models.ForeignKey(GoodsCategory, on_delete=models.CASCADE,
                                 related_name='category', verbose_name='商品类目')
    goods = models.ForeignKey(Goods, on_delete=models.CASCADE, related_name='goods')

    class Meta:
        verbose_name = '首页广告'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.goods.name



3-4 trade交易的model设计

下面的效果就和from users.model import userprofile 的意思一样。点击到函数里面,他也是使用setting中定义的变量来导入
User = get_user_model()


class ShoppingCart(models.Model):
    """
    购物车
    """
    user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户')
    goods = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name='商品')
    nums = models.IntegerField('购买数量', default=0)
    add_time = models.DateTimeField(default=datetime.now, verbose_name='添加时间')

    class Meta:
        verbose_name = '购物车'
        verbose_name_plural = verbose_name
        unique_together = ('user', 'goods')

    def __str__(self):
        return "%s(%d)" .format(self.goods.name, self.nums)



class OrderInfo(models.Model):
    """
    订单信息
    """
    ORDER_STATUS = (
        ('TRADE_SUCCESS', '成功'),
        ('TRADE_CLOSED', '超时关闭'),
        ('WAIT_BUYER_PAY', '交易创建'),
        ('TRADE_FINISHED', '交易结束'),
        ('PAYING', '待支付')
    )

    PAY_TYPE = (
        ('alipay', '支付宝'),
        ('wechat', '微信')
    )

    user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户')
    order_sn = models.CharField('订单编号', max_length=30, null=True, blank=True, unique=True)
    nonce_str = models.CharField('随机加密串', max_length=50, null=True, blank=True, unique=True)
    trade_no = models.CharField('交易号', max_length=100, unique=True, null=True, blank=True)
    pay_status = models.CharField('订单状态', choices=ORDER_STATUS, default='PAYING', max_length=30)
    pay_type = models.CharField('支付类型', choices=PAY_TYPE, default='alipay', max_length=10)
    post_script = models.CharField('订单留言', max_length=200)
    order_mount = models.FloatField('订单金额', default=0.0)
    pay_time = models.DateTimeField('支付时间', null=True, blank=True)

    address = models.CharField('收货地址', max_length=100, default='')
    signer_name = models.CharField('签收人', max_length=20, default='')
    signer_mobile = models.CharField('联系电话', max_length=11)

    add_time = models.DateTimeField('添加时间', default=datetime.now)

    class Meta:
        verbose_name = '订单信息'
        verbose_name_plural = verbose_name

    def __str__(self):
        return str(self.order_sn)


class OrderGoods(models.Model):
    """
    订单内的商品详情
    """
    # 一个订单对应多个商品
    order = models.ForeignKey(OrderInfo, on_delete=models.CASCADE, verbose_name='订单信息', related_name='goods')
    goods = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name='商品')
    goods_num = models.IntegerField('商品数量', default=0)

    add_time = models.DateTimeField('添加时间', default=datetime.now)

    class Meta:
        verbose_name = '订单商品'
        verbose_name_plural = verbose_name

    def __str__(self):
        return str(self.order.order_sn)

        
        
        
        
        
        
        
        
        
        
        
        

3-5 用户操作的model设计

class UserFav(models.Model):
    """
    用户收藏操作
    """
    user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户')
    goods = models.ForeignKey(Goods, on_delete=models.CASCADE, verbose_name='商品', help_text='商品id')
    add_time = models.DateTimeField('添加时间', default=datetime.now)

    class Meta:
        verbose_name = '用户收藏'
        verbose_name_plural = verbose_name
        unique_together = ('user', 'goods')

    def __str__(self):
        return self.user.username


class UserAddress(models.Model):
    """
    用户收货地址
    """
    user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户')
    province = models.CharField('省份', max_length=100, default='')
    city = models.CharField('城市', max_length=100, default='')
    district = models.CharField('区域', max_length=100, default='')
    address = models.CharField('详细地址', max_length=100, default='')
    signer_name = models.CharField('签收人', max_length=100, default='')
    signer_mobile = models.CharField('电话', max_length=11, default='')
    add_time = models.DateTimeField('添加时间', default=datetime.now)

    class Meta:
        verbose_name = '收货地址'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.address


class UserLeavingMessage(models.Model):
    """
    用户留言
    """
    MESSAGE_CHOICES = (
        (1, '留言'),
        (2, '投诉'),
        (3, '询问'),
        (4, '售后'),
        (5, '求购')
    )

    user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='用户')
    message_type = models.IntegerField(default=1, choices=MESSAGE_CHOICES, verbose_name='留言类型',
                                       help_text=u"留言类型: 1(留言),2(投诉),3(询问),4(售后),5(求购)")
    subject = models.CharField('主题', max_length=100, default='')
    message = models.TextField('留言内容', default='', help_text='留言内容')
    file = models.FileField(upload_to='message/images', verbose_name='上传的文件', help_text='上传的文件')
    add_time = models.DateTimeField('添加时间', default=datetime.now)

    class Meta:
        verbose_name = '用户留言'
        verbose_name_plural = verbose_name

    def __str__(self):
        return self.subject






3-6 migrations原理及表生成


一定要将创建的app添加到installapp里面

python manage.py makemigrations

installapp里面可以设置成下面的
goods.apps.Goodsconfig

django的migration操作记录都放到了数据库里面的migration表里面的,如果需要修改表字段,可以试着将migration表里面对应的行删除,重新生成

django建表的时候出现sql_mode的警告
https://www.jianshu.com/p/bc41a8bf9d9b

python manage.py migrate





3-7 xadmin后台管理系统的配置
要使用老师的extra_apps里面的xadmin源码,官网上面的有bug
https://github.com/sshwsfc/xadmin

还有每个app里面的xadmin文件拷贝到对应的文件中

installed_app添加
url添加
安装依赖,使用官网的依赖,除了django,其他的都安装最新的版本

下面的依赖要装
pip install django-crispy-forms django-import-export django-reversion django-formtools future httplib2 six xlwt xlsxwriter

python manage.py createsuperuser     admin admin1234
python manage.py runserver 
配置文件:ALLOWED_HOSTS = ['192.168.170.30','localhost']
pycharm里面设置:run-edit config-监听地址:0.0.0.0





3-8 导入商品类别数据

这个是单独使用django的model
写完后,直接python xxx.py来导入数据

3-9 导入商品和商品类别数据-2

具体查看代码,这里跳过






第4章 vue的结构和restful api介绍
本章介绍了一些基础概念,包括restfulapi以及vue前端项目中的一些概念, 以及vue前端项目的组织结构介绍。了解这些概念会让大家在后续的章节中遇到对应的名词时候不会感觉到陌生

4-1 restful api介绍
http://www.ruanyifeng.com/blog/2014/05/restful_api.html
http://www.ruanyifeng.com/blog/2011/09/restful.html

4-2 vue的基本概念介绍
4-3 vue源码结构介绍

跳过,还是要自己看,讲的都是基础的







第5章 商品列表页
本章非常详细的介绍了如何从django开始一步步深入到drf去完成商品的列表页功能, 本章节是课程最重要的一章。也是drf学习的基础。本章节从列表
页的功能作为突破点去介绍drf的最重要的一些概念,如apiview, viewsets,router,serializer、分页、过滤等功能, 通过这些知识会让大家对drf
有一个整体的概念。 ...

5-1 django的view实现商品列表页

django的中文官方文档很全,值得学习


新建文件:apps/goods/view_base.py
下面是一个简单的示例,查询商品信息, 将字典转换成json,返回给用户
from django.views.generic import View
from goods.models import Goods

class GoodsListView(View):
    def get(self, request):
        json_list = []
        goods = Goods.objects.all()[:5]
        for good in goods:
            json_dict = {}
            json_dict['name'] = good.name
            json_dict['category'] = good.category.name
            json_dict['market_price'] = good.market_price
            #json_dict['add_time'] = good.add_time
            json_list.append(json_dict)

        from django.http import HttpResponse
        import json

        return HttpResponse(json.dumps(json_list), content_type='application/json')


        
        
5-2 django的serializer序列化model       
但是这里面的add_time字段不能被序列化,包括图片链接等,会报错。可以使用下面的来实现,先序列化将model的queryset转换为字典,再将字段转换为json
下面是另外一种实现方式,下面的方式缺点:格式是定死的,无法修改。以及显示特定字段等问题
from django.views.generic import View
from goods.models import Goods

class GoodsListView(View):
    def get(self, request):
        json_list = []
        goods = Goods.objects.all()[:5]

        import json
        from django.core  import serializers
        from django.http import JsonResponse

        json_data = serializers.serialize('json', goods)
        json_data = json.loads(json_data)
        return JsonResponse(json_data, safe=False)   #当序列化非字典的格式的时候,需要添加safe=False,应该是因为这里最后显示的是数组,而不是字典





5-3 apiview方式实现商品列表页-1
5-4 apiview方式实现商品列表页-2
新建文件:apps/goods/view.py,下面都在这里面进行操作

根据官方文档,https://www.django-rest-framework.org/ 进行下面的操作

需要添加‘crispy_forms’和‘rest_framework’到install_app里面,

安装下面的包
coreapi (1.32.0+) - Schema generation support.  用于生成文档
Markdown (2.1.0+) - Markdown support for the browsable API.
django-filter (1.0.1+) - Filtering support.
django-crispy-forms - Improved HTML display for filtering.
django-guardian (1.1.1+) - Object level permissions support.


将文档url添加到url.py文件里面,将认证的url添加到里面
from rest_framework.documentation import include_docs_urls

url(r'docs/', include_docs_urls(title='暮学生鲜')),
如果访问网页出错,可能是django版本的问题,升级版本到1.11.6 :pip install --upgrade django==1.11.6



添加文件apps/goods/serializers.py 里面专门放置序列化类
经过下面的配置后,访问页面就能正常显示goods商品列表了,还是比较方便的

from rest_framework import serializers

class GoodsSerializer(serializers.Serializer):
    name = serializers.CharField(required=True,max_length=100)
    click_num = serializers.IntegerField(default=0)
    goods_front_image = serializers.ImageField()


view.py文件
from .models import Goods
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import GoodsSerializer


class GoodsListView(APIView):
    def get(self, request):
        goods = Goods.objects.all()[:5]
        goods_serializer = GoodsSerializer(goods, many=True)
        return Response(goods_serializer.data)

url.py文件
urlpatterns = [
    url(r'^xadmin/', xadmin.site.urls),
    url(r'media/(?P<path>.*)$', serve, {'document_root': MEDIA_ROOT}),
    url(r'goods', GoodsListView.as_view(), name='goods-list'),
    url(r'docs/', include_docs_urls(title='暮学生鲜')),
    url(r'^api-auth/', include('rest_framework.urls')),  #必须加这个,不加这个,页面不会出现登录的按钮
]




5-5 drf的modelserializer实现商品列表页功能

下面的示例是用户往server端传数据,这里的post方法拿到用户数据,通过序列化进行检查,检查正确后,执行:serializer.save(),因为GoodsSerializer里
面重写了create方法,也就是说执行save方法其实就是执行用户自定义的create方法。
这里要理解类的继承和重写

view文件如下
from .models import Goods
from rest_framework.views import APIView
from rest_framework.response import Response
from .serializers import GoodsSerializer
from rest_framework import status

class GoodsListView(APIView):
    def get(self, request):
        goods = Goods.objects.all()[:5]
        goods_serializer = GoodsSerializer(goods, many=True)
        return Response(goods_serializer.data)

    def post(self, request):
        serializer = GoodsSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            return Response(serializer.data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)


serializers.py内容如下
from rest_framework import serializers
from .models import Goods


class GoodsSerializer(serializers.Serializer):
    name = serializers.CharField(required=True,max_length=100)
    click_num = serializers.IntegerField(default=0)
    goods_front_image = serializers.ImageField()

    def create(self, validated_data):
        return Goods.objects.create(**validated_data)



下面使用另外一种更加简单的序列化:serializers.ModelSerializer  不懂的可以看看前面的基础课程
当想展示goods表中的其他外键对应的内容时候,可以使用下面的
在GoodsSerializer里面将category生成一个对象category = CategorySerializer(),作为一个字段传递到goods序列化中,这样category里面的所有内容都会显示到json中

serializers.py文件内容
from rest_framework import serializers
from .models import Goods, GoodsCategory


class CategorySerializer(serializers.ModelSerializer):
    class Meta:
        model = GoodsCategory
        fields = '__all__'


class GoodsSerializer(serializers.ModelSerializer):
    category = CategorySerializer()
    class Meta:
        model = Goods
        fields = '__all__'

或者下面的也可以
class GoodsSerializer(serializers.ModelSerializer):
    category = CategorySerializer()
    class Meta:
        model = Goods
        fields = '__all__'
        depth = 2 #更简洁,但是不知道原理





5-6 GenericView方式实现商品列表页和分页功能详解

下面是views.py文件内容,可以达到上面相同的效果,具体还是要看里面的源码
from rest_framework import generics, mixins

class GoodsListView(mixins.ListModelMixin,
                  generics.GenericAPIView):
    queryset = Goods.objects.all()[:5]
    serializer_class = GoodsSerializer

    def get(self, request, *args, **kwargs):
        return self.list(request, *args, **kwargs)




5-7 viewsets和router完成商品列表页
路由请求get方法对应后面不同类的方法


5-8 drf的Apiview、GenericView、Viewset和router的原理分析

from rest_framework.viewsets import ModelViewSet
下面是子类以及子类对应的父类
ModelViewSet             - drf 自带的
    GenericViewSet       - drf 自带的
        GenericAPIView   - drf 自带的
            APIView      - drf 自带的
                View     - django 自带的

class ModelViewSet(mixins.CreateModelMixin,
                   mixins.RetrieveModelMixin,
                   mixins.UpdateModelMixin,
                   mixins.DestroyModelMixin,
                   mixins.ListModelMixin,
                   GenericViewSet):



5-9 drf的request和response

讲里面的一些常用的属性



5-10 drf的过滤

下面是一个简单的过滤方法,通过重写GenericViewSet里面的get_queryset来实现
self.request.query_params.get方法可以获取url里面参数
然后根据这个参数进行后面的处理

class GoodsListView(mixins.ListModelMixin,
                  GenericViewSet):
    queryset = Goods.objects.all()
    serializer_class = GoodsSerializer

    def get_queryset(self):
        queryset = Goods.objects.all()
        price_min = self.request.query_params.get('price_min', 0)
        if price_min:
            queryset = queryset.filter(shop_price__gt=int(price_min))
        return queryset

通过下面的url进行测试:http://127.0.0.1:8000/goods?format=json&price_min=237



下面使用django-filter进行测试,更简单
https://www.django-rest-framework.org/api-guide/filtering/#djangofilterbackend
首先进行安装:pip install django-filter
添加到install_app:django_filters


感觉没用,后面用到的时候再说









5-11 drf的搜索和排序
5-12 总结

未经允许不得转载:江哥架构师笔记 » drf学习:Vue+Django REST framework打造生鲜电商项目1-5章

分享到:更多 ()

评论 抢沙发

评论前必须登录!