需求
做一个电商项目,就是卖不同品牌的电脑,可以根据不同的品牌进行查询,还有对订单的crud,还有用户的会员中心功能模块。还有收货地址模块
创建一个Django项目
用pycharm直接创建一个Django项目
配置基础东西
连接mysql
使用Django_rest
解决跨域
第一步
第二部
第三部
建模(model里面写实体类)
from django.db import models
from django.db import models
from django.conf import settings
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'
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'
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'
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'
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'
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自带的后台里面
from django.contrib import admin
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自带的后台管理界面
手动在后台管理系统里面添加相关数据
商品列表后台接口
序列器
class ProductListSerializer(serializers.ModelSerializer):
class Meta:
model=Product
fields=('id','model','image','price','sold','category','manufacturer',)
view代码
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')
pagination_class = LimitOffsetPagination
url代码
url(r'^product_list/$',views.ProductListView.as_view(),name='product_list'),
实现分页(传limit offset)
REST_FRAMEWORK = {
'DEFAULT_PAGINATION_CLASS':'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 2,
}
以上是默认的每一页有3个数据
也就是前端调用后端的这个接口,默认给返回3条数据,使用的接口是
http://127.0.0.1:8000/computer/product_list/
那么既然项目可以使用分页,前端如何使用分页功能呢?也就是想要第二页,第三页的数据,我们如何使用呢?
http://127.0.0.1:8000/computer/product_list/?offset=6
前端如何限制每一页有几条数据,可以使用limit 前提是view里面要写这个
pagination_class = LimitOffsetPagination
以上就可以实现,对于列表展示的分页功能
实现根据字段进行排序和模糊搜索
以上可以实现对列表的查询,并且实现了分页,但是现在我们想要根据某一个字段进行排序和模糊搜索,我们可以在对应的接口里面的view代码里面这样配置
filter_backends = (OrderingFilter,SearchFilter)
ordering_fields=('category','manufacturer','created','sold',)
search_fields=('description','model')
这样写了之后,前端调用这个接口,需要根据某一个字段进行排序,可以这样传
http://127.0.0.1:8000/computer/product_list/?ordering=created
http://127.0.0.1:8000/computer/product_list/?search=华为手机01
商品分类查询
因为商品表里面有一个外键,就是产品的分类
view代码
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):
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代码
url(r'^product_list_by_category/$',views.ProductListByCategoryView.as_view(),name='productlistbycategory'),
前端调用的书写
http://127.0.0.1:8000/computer/product_list_by_category/?category=6
product_list_by_category就是我们第二个接口的名字,category是分类字段,当前数据库中有2个分类
category 根据这个字段,查询对应分类的产品
在分类查询的基础上面,需要排序
比如根据价格排序
根据生产厂商进行选择
view
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
url(r'^product_list_by_category_manufacturer/$',views.ProductListByCategoryManufacturerView.as_view(),name='product_list_by_category_manufacturer'),
前段传参
商品的详情
也就是根据列表数据的id值,进行查询数据库,将单个数据的详情进行返回给前端
view
class ProductRetrieveView(generics.RetrieveAPIView):
'''产品详情'''
queryset = Product.objects.all()
serializer_class = ProductRetrieveSerializer
permission_classes = (permissions.AllowAny,)
url
url(r'^product_retrieve/(?P<pk>[0-9]+)/$', views.ProductRetrieveView.as_view(),name='product_retrieve'),
参数是pk 他的值就是数据的id,路径的这个接参属性必须是pk
前端如何传参
查询的外键需要返回具体的name值,而不是id
第一种方法
class ProductRetrieveSerializer(serializers.ModelSerializer):
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框架自带
配置完成,进行生成数据库迁移 数据库里面会生成
就会多一个这个,配置文件里面,多加这个
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里面写
url('^api-token-auth/',views.obtain_auth_token),
前段调用 http://127.0.0.1:8000/api-token-auth/,这个将用户名密码传过去,就可以实现token登录
用户的详情
用户详情序列器
class UserInfoSerializer(serializers.ModelSerializer):
profile_of=UserProfileSerializer()
class Meta:
model=User
fields=('id','username','email','first_name','last_name','date_joined','profile_of',)
view
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
url(r'^user_info/$',views.UserInfoView.as_view(),name='user_info'),
前端如何做
用户登录成功,就跳转到用户详情页面,在已进入这个页面,那就调用用户详情的接口,在这个接口的view里面进行权限限制
如果成功,就回显数据,如果失败,那么就跳转到登录页面
用户的注册
往用户表和会员档案里面保存数据
序列化
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
class UserCreateView(generics.CreateAPIView):
'''用户创建'''
serializer_class = UserSerializer
url
url(r'^user_create/$', views.UserCreateView.as_view(),
name='user_create'),
用户信息的更新
序列器
class UserProfileSerializer(serializers.ModelSerializer):
class Meta:
model=UserProfile
fields=('id','user','mobile_phone','nickname','description','icon','created','updated',)
read_only_fields=('user',)
view
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
url(r'^user_profile_ru/(?P<pk>[0-9]+)/$', views.UserProfileRUView.as_view(),name='user_profile_ru'),
收货地址的新增和列表查询
新增和list列表查询 (一个接口实现)
新增收货地址,不仅仅要在地址表里面新增,而且还要将用户档案里面的地址变为当前最新新增的。
传了参数,并且是put请求 ,就是新增 不传,直接调用,get请求,就是列表查询
序列器
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
class DeliveryAddressLCView(generics.ListCreateAPIView):
'''收货地址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):
user=self.request.user
s=serializer.save(user=user)
profile=user.profile_of
profile.delivery_address=s
profile.save()
url
url(r'^delivery_address_lc/$',views.DeliveryAddressLCView.as_view(),name='delivery_address_lc'),
页面展示
收货地址的回显,修改,删除(一个接口实现)
传的method不一样
view
class DeliveryAddressRUDView(generics.RetrieveUpdateDestroyAPIView):
'''收货地址RUD'''
serializer_class = DeliveryAddressSerilizer
permission_classes = (permissions.IsAuthenticated,)
def get_object(self):
user = self.request.user
try:
obj=DeliveryAddress.objects.get(id=self.kwargs['pk'],user=user)
except Exception as e:
raise NotFound('no found')
return obj
url
url(r'^delivery_address_rud/(?P<pk>[0-9]+)/$', views.DeliveryAddressRUDView.as_view(),name='delivery_address_rud'),
购物车的查询新增
购物车是一张表,这个表和订单表是同一个表,只是状态是不一样的。这个表有一个外键和用户关联。
新增购物车
序列器
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
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
url(r'^order_create/$',views.OrderCreateView.as_view(),name='order_create'),
购物车查询
序列器
class OrderListSerializer(serializers.ModelSerializer):
product = ProductListSerializer()
address = DeliveryAddressSerilizer()
class Meta:
model=Order
fields=('id','status','user','product','price','quantity','remark','address','created','updated',)
view
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
url(r'^cart_list/$',views.CartListView.as_view(),name='cart_list'),
订单
和购物车同一张表,只是状态不一样
订单列表展示
序列器
class OrderListSerializer(serializers.ModelSerializer):
product = ProductListSerializer()
address = DeliveryAddressSerilizer()
class Meta:
model=Order
fields=('id','status','user','product','price','quantity','remark','address','created','updated',)
view
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
url(r'^order_list/$',views.OrderListView.as_view(),name='order_list'),
下单操作,删除操作
view
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
url(r'^order_rud/(?P<pk>[0-9]+)/$', views.OrderRUDView.as_view(), name='order_update'),
|