天天生鲜 - App设计
一、项目目录
daily_fresh_demo - daily_fresh_demo - df_cart # 购物车功能 - df_goods # 商品功能 - df_order # 订单功能 - df_user # 用户功能(包括登录验证等相关功能) - static # 静态文件 - templates # 前段模板- whoosh_index # 全局索引文件 db.sqlite3 manage.py
注:这个电商网站是博主在接触Django之后做的第一个项目,还有很多地方需要改进。如果有需要项目相关视频资源的朋友可以博客园私信,或者评论区留言,博主会在看到的第一时间分享。
附github源码地址(包含静态文件):https://github.com/weilanhanf/daily_fresh_demo
daily_fresh_demo - daily_fresh_demo - settings.py - urls.py - wsgi.py - __init__.py # 这里使用Django自带的小型sqlite数据库,如果使用mysql就需要在这里添加相应的驱动
1、全局配置文件settings.py
import os # Build paths inside the project like this: os.path.join(BASE_DIR, ...) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # Quick-start development settings - unsuitable for production # See https://docs.djangoproject.com/en/2.0/howto/deployment/checklist/ # SECURITY WARNING: keep the secret key used in production secret! SECRET_KEY = 'uey!i4x26n!$d-73cs%blri)09#xfud_e361ne2h(#s27)l!' # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True ALLOWED_HOSTS = [] # Application definition INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'df_user', 'df_goods', 'df_cart', 'df_order', 'tinymce',#使用富文本编辑框要在settings文件中安装 'haystack',#全文检索 ] MIDDLEWARE = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.common.CommonMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware', ] ROOT_URLCONF = 'daily_fresh_demo.urls' TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates')] , 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ] WSGI_APPLICATION = 'daily_fresh_demo.wsgi.application' # Database # https://docs.djangoproject.com/en/2.0/ref/settings/#databases DATABASES = { 'default': { 'ENGINE': 'django.db.backends.sqlite3', 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }, 'OPTIONS':{ 'TIMEOUT': 20, } } # Password validation # https://docs.djangoproject.com/en/2.0/ref/settings/#auth-password-validators AUTH_PASSWORD_VALIDATORS = [ { 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', }, { 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', }, { 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', }, { 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', }, ] # Internationalization # https://docs.djangoproject.com/en/2.0/topics/i18n/ LANGUAGE_CODE = 'en-us' TIME_ZONE = 'UTC' USE_I18N = True USE_L10N = True USE_TZ = True # Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/2.0/howto/static-files/ STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static'), ] #开发阶段上传文件目录 MEDIA_ROOT = os.path.join(BASE_DIR, 'static') #部署后的上传文件目录 # MEDIA_ROOT = 'var/www/daily_fresh_demo/static' #富文本编辑框的使用配置 TINYMCE_DEFAULT_CONFIG = { 'theme': 'advanced', 'width': 600, 'height': 400, } HAYSTACK_CONNECTIONS = { 'default':{ #使用whoosh引擎 'ENGINE':'haystack.backends.whoosh_cn_backend.WhooshEngine', #添加索引文件路径 'PATH':os.path.join(BASE_DIR, 'whoosh_index'), } } #当修改删除数据时,自动生成索引 HAYSTACK_SIGNAL_PROCESSOR ='haystack.signals.RealtimeSignalProcessor' # HAYSTACK_DEFAULT_OPERATOR = 'OR' HAYSTACK_SEARCH_RESULTS_PER_PAGE = 18#每一页显示多少数据
2、路由分发urls.py
from django.contrib import admin from django.urls import path from django.conf.urls import url, include urlpatterns = [ path('admin/', admin.site.urls), url(r'^', include('df_goods.urls',namespace='goods')), url(r'^user/', include('df_user.urls', namespace='user')), url(r'^goods/', include('df_goods.urls')), url(r'^cart/',include('df_cart.urls', namespace='cart')), url(r'^order/',include('df_order.urls', namespace='order')), url(r'^search/', include('haystack.urls')),#全文检索 url(r'^tinymce/', include('tinymce.urls')),#使用富文本编辑框配置confurl ]
二、用户相关功能模块
app目录如下
df_user - migrations # 迁移文件目录 - admin.py - apps.py - models.py - test.py - urls.py - user_docorator.py # 这里使用装饰器做用户身份认证 - views.py - __init__.py
1、apps.py
from django.apps import AppConfig class DfUserConfig(AppConfig): name = 'df_user'
2、models.py
from django.db import models # Create your models here. class UserInfo(models.Model): uname=models.CharField(max_length=20) upwd=models.CharField(max_length=40) uemail=models.CharField(max_length=30) ushou=models.CharField(max_length=20,default="") uaddress=models.CharField(max_length=100,default="") uyoubian=models.CharField(max_length=6,default="") uphone=models.CharField(max_length=11,default="") # default,blank是python层面的约束,不影响数据库表结构,修改时不需要迁移 python manage.py makemigrations class GoodsBrowser(models.Model): user=models.ForeignKey('UserInfo', on_delete=models.CASCADE) good=models.ForeignKey('df_goods.GoodsInfo', on_delete=models.CASCADE)
3、urls.py
#!/user/bin/env python # -*- coding: utf-8 -*- from django.conf.urls import url from . import views app_name = 'df_user' urlpatterns = [ url(r'^register/$', views.register), url(r'^register_handle/$', views.register_handle), url(r'^register_exist/$', views.register_exist), url(r'^login/$', views.login), url(r'^login_handle/$', views.login_handle), url(r'^info/$', views.info), url(r'^order/(\d+)$', views.order), url(r'^site/$', views.site), # url(r'^place_order/$', views.place_order), url(r'^logout/$', views.logout) ]
4、view.py
from django.shortcuts import render, redirect, HttpResponseRedirect, HttpResponse from django.http import JsonResponse from .models import UserInfo from df_goods.models import GoodsInfo from df_user.models import GoodsBrowser from df_order.models import * from hashlib import sha1 from . import user_decorator from django.core.paginator import Paginator,Page def register(request): context={ 'title':'用户注册', } return render(request, 'df_user/register.html', context) def register_handle(request): #接受用户输入 post = request.POST print(request.method) uname=post.get('user_name') upwd=post.get('pwd') upwd2=post.get('cpwd') uemail=post.get('email') #判断两次密码一致性 if upwd != upwd2: return redirect('/user/register/') #密码加密 s1=sha1() s1.update(upwd.encode('utf8')) upwd3=s1.hexdigest() # sha = hashlib.sha1(upwd.encode('utf8')) # sha.hexdigest() #创建对象 user=UserInfo() user.uname=uname user.upwd=upwd3 user.uemail=uemail user.save() print(uname, upwd3,uemail) #注册成功 context = { 'title': '用户登陆', 'uname': uname, } # return redirect('/user/login/') return render(request, 'df_user/login.html', context) def register_exist(request): uname=request.GET.get('uname') count=UserInfo.objects.filter(uname=uname).count() if count == 0: print('当前用户名可用') return JsonResponse({'count':count}) # @user_decorator.request_detail def login(request): print(request.get_full_path(), 'request.get_full_path') uname=request.COOKIES.get('uname', '') context={ 'title': '用户登陆', 'error_name':0, 'error_pwd':0, 'uname':uname, } return render(request, 'df_user/login.html', context) def login_handle(request):#没有利用ajax提交表单 #接受请求信息 post = request.POST uname = post.get('username') upwd = post.get('pwd') jizhu = post.get('jizhu', 0) #根据用户名查询对象 # print(uname, upwd, jizhu, request.method) users = UserInfo.objects.filter(uname=uname)#[] print(uname,len(users), users) #判断如果未查到则用户名错误,如果查到则判断密码是否正确,正确则转到用户中心 if len(users)==1: s1 = sha1() s1.update(upwd.encode('utf8')) if s1.hexdigest()==users[0].upwd: print("验证成功") # request.COOKIES['url'] = '/8/' url = request.COOKIES.get('url','/') print(url) red = HttpResponseRedirect(url)#继承与HttpResponse 在跳转的同时 设置一个cookie值 #是否勾选记住用户名,设置cookie if jizhu!=0: red.set_cookie('uname', uname) # print('设置cookie', request.COOKIES['uname']) else: red.set_cookie('uname', '',max_age=-1)#设置过期cookie时间,立刻过期 request.session['user_id'] = users[0].id request.session['user_name'] = uname return red else: context = { 'title':'用户名登陆', 'error_name': 0, 'error_pwd':1, 'uname':uname, 'upwd':upwd, } # print('密码错误') return render(request, 'df_user/login.html', context) else: context = { 'title': '用户名登陆', 'error_name': 1, 'error_pwd': 0, 'uname': uname, 'upwd': upwd, } print('不存在当前用户') return render(request, 'df_user/login.html', context) def logout(request): request.session.flush()#清空当前用户所有session return redirect('/') @user_decorator.login def info(request): username =request.session.get('user_name') # print(username) user = UserInfo.objects.filter(uname = username).first() # user = UserInfo.objects.get(id=request.session['user_id']) # print(request.session['user_name']) #列表形式最近浏览 # goods_ids = request.COOKIES.get('goods_ids', '') # print('cookies', goods_ids) #在cookie中goods_id以{ 'gooids':'1,5,6,7,8,9'}形式存入 # goods_ids1 = goods_ids.split(',')#拆分为列表 # print('最近浏览商品序号',goods_ids1) # goods_list1 = GoodsInfo.objects.filter(id__in=goods_ids1)#会破坏浏览商品的先后顺序 # if goods_ids1[0] != '' : # goods_list = [GoodsInfo.objects.get(id=int(goods_id)) for goods_id in goods_ids1] # # for goods_id in goods_ids1: # # goods_list.append(GoodsInfo.objects.get(id=int(goods_id)))#pk与id区别 # # 每次只查询一个商品并放入列表的最后,保证了浏览商品的顺序 # explain = '最近浏览' # else: # goods_list = [] # explain = '无最近浏览' # 最近浏览计入第三张那个表 goods_ids = GoodsBrowser.objects.filter(user_id=request.session['user_id']) # print(goods_ids) goods_ids1 = [good_browser.good_id for good_browser in goods_ids] # print(goods_ids1) # goods_ids2 = [] # for good_id in goods_ids1: # if good_id not in goods_ids2: # goods_ids2.append(good_id) # print(goods_ids2) if len(goods_ids1) != 0: goods_list = [GoodsInfo.objects.get(id=goods_id) for goods_id in goods_ids1] goods_list.reverse() # print(goods_list) explain = '最近浏览' else: goods_list = [] explain = '无最近浏览' context={ 'title':'用户中心', 'page_name': 1, 'user_phone':user.uphone, 'user_address':user.uaddress, 'user_name':request.session['user_name'], 'goods_list': goods_list, 'explain': explain, } # print(user.uname, user.uaddress, user.uphone) return render(request, 'df_user/user_center_info.html', context) @user_decorator.login def order(request, index): user_id = request.session['user_id'] orders_list = OrderInfo.objects.filter(user_id=int(user_id)).order_by('-odate') # print(len(orders_list)) # print(orders_list) paginator = Paginator(orders_list,2) page = paginator.page(int(index)) context={ 'paginator': paginator, 'page':page, # 'orders_list':orders_list, 'title':"用户中心", 'page_name':1, } return render(request, 'df_user/user_center_order.html', context) @user_decorator.login def site(request): user = UserInfo.objects.get(id=request.session['user_id']) # print(user, type(user), user.uphone,user.uaddress) if request.method=="POST": post = request.POST user.ushou = post.get('ushou') user.uaddress = post.get('uaddress') user.uyoubian = post.get('uyoubian') user.uphone = post.get('uphone') user.save() context = { 'page_name': 1, 'title': '用户中心', 'user':user, } return render(request, 'df_user/user_center_site.html', context)
5、user_decorator.py
对用户进行身份认证,如用户进入个人中心的时候需要验证,购买商品也需要身份验证
#!/user/bin/env python # -*- coding: utf-8 -*- from django.http import HttpResponseRedirect import re #如果未登录则转到登陆页面 def login(func): def login_fun(request, *args, **kwargs): if 'user_id' in request.session: return func(request, *args, **kwargs) else: red = HttpResponseRedirect('/user/login/') red.set_cookie('url', request.get_full_path()) print(request.get_full_path(), 'user_decorator') #保证用户再登陆验证之后仍点击到希望的页面 return red return login_fun """ http://127.0.0.1:8000/200/?type=10 request.path :表示当前路径,为/200/ request.get_full_path():表示完整路径,为/200/?type=10 """
三、商品相关功能模块
app目录如下
df_goods - migrations - admin.py - apps.py - models.py - search_indexes.py # 搜索商品功能 - tests.py - urls.py - views.py - __init__.py
1、app.py
from django.apps import AppConfig class DfGoodsConfig(AppConfig): name = 'df_goods'
2、models.py
from django.db import models from tinymce.models import HTMLField#使用富文本编辑框要在settings文件中安装 #将一对多的关系维护在GoodsInfo中维护,另外商品信息与分类信息都属于重要信息需要使用逻辑删除 # Create your models here. class TypeInfo(models.Model):#商品分类信息 水果 海鲜等 isDelete = models.BooleanField(default=False)#逻辑删除 ttitle = models.CharField(max_length=20) def __str__(self):#这里定义在admin中要显示的内容 # return self.ttitle.encode('utf-8') return self.ttitle class GoodsInfo(models.Model):#具体商品信息 isDelete = models.BooleanField(default=False)#逻辑删除 gtitle = models.CharField(max_length=20)#商品的名称 gpic = models.ImageField(upload_to='df_goods')#关联图片目录 gprice = models.DecimalField(max_digits=5, decimal_places=2)#商品价格小数位为两位,整数位为3位 gunit = models.CharField(max_length=20, default='500g')#商品单位kg或者个数 gclick = models.IntegerField()#商品点击量 gjianjie = models.CharField(max_length=200)#商品简介 gkucun = models.IntegerField()#商品库存 gcontent = HTMLField()#商品介绍 gtype = models.ForeignKey(TypeInfo, on_delete=models.CASCADE)#外键关联TypeInfo表 # gadv = models.BooleanField(default=False)#商品是否推荐 def __str__(self): # return self.gtitle.encode('utf-8') return self.gtitle # python3中 __str__ 不能接收bytes类型的数据,这和python2/3的编解码方式是有关系的。
3、urls.py
#!/user/bin/env python # -*- coding: utf-8 -*- from django.conf.urls import url from . import views from .views import * app_name = 'df_goods' urlpatterns = [ url('^$', views.index), url('^list(\d+)_(\d+)_(\d+)/$', views.list), url('^(\d+)/$', views.detail), url(r'^search/', MySearchView()),#全文检索 url(r'^cookieTest/', views.cookieTest) ]
4、view.py
from django.shortcuts import render, HttpResponse from .models import * from django.core.paginator import Page, Paginator from df_cart.models import CartInfo from df_user.models import GoodsBrowser from haystack.views import SearchView # Create your views here. def index(request): #查询各个分类的最新4条,最热4条数据 typelist = TypeInfo.objects.all() print(len(typelist), 'asdf') # 连表操作(了不起的双下划线)利用双下划线和 _set将表之间的操作连接起来 type0 = typelist[0].goodsinfo_set.order_by('-id')[0:4]#按照最新上传的水果显示 type01 = typelist[0].goodsinfo_set.order_by('-gclick')[0:4]#按照用户点击量上传 type1 = typelist[1].goodsinfo_set.order_by('-id')[0:4] type11 = typelist[1].goodsinfo_set.order_by('-gclick')[0:4] type2 = typelist[2].goodsinfo_set.order_by('-id')[0:4] type21 = typelist[2].goodsinfo_set.order_by('-gclick')[0:4] type3 = typelist[3].goodsinfo_set.order_by('-id')[0:4] type31 = typelist[3].goodsinfo_set.order_by('-gclick')[0:4] type4 = typelist[4].goodsinfo_set.order_by('-id')[0:4] type41 = typelist[4].goodsinfo_set.order_by('-gclick')[0:4] type5 = typelist[5].goodsinfo_set.order_by('-id')[0:4] type51 = typelist[5].goodsinfo_set.order_by('-gclick')[0:4] #判断是否存在登录状态 try: user_id = request.session['user_id'] cart_count = CartInfo.objects.filter(user_id=int(user_id)).count except: cart_count = 0 context = { 'title': '首页', 'cart_count': cart_count, 'guest_cart':1, 'type0':type0, 'type01':type01, 'type1':type1, 'type11':type11, 'type2':type2, 'type21':type21, 'type3':type3, 'type31':type31, 'type4':type4, 'type41':type41, 'type5':type5, 'type51':type51, } """ context = { 'guest_cart':1, 'title': '首页' } #获取最新的4个商品 hot = GoodsInfo.objects.all().order_by('-gclick')[0:4] context.setdefault('hot', hot) #*******获取各分类下的点击商品******* #首先获取分类 typelist = TypeInfo.objects.all() for i in range(len(typelist)): #获取type对象 type = typelist[i] #根据type对象获取商品列表 #通过外键关联获取商品 #获取对应列表中的通过id倒序排列的前四个 goods1 = type.goodinfo_set.order_by('-id')[0:4] goods2 = type.goodinfo_set.order_by('-gclick')[0:4] key1 = 'type' + str(i) # 根据id 倒叙排列 key2 = 'type' + str(i) + str(i) # 根据点击量倒序排列 context.setdefault(key1, goods1) context.setdefault(key2, goods2) print(context) """ # print(type0, type01) # for i in type0: # print(i.gpic) return render(request, 'df_goods/index.html', context) def list(request, tid, pindex, sort): #tid:商品种类信息 pindex:商品页码 sort:商品显示分类方式 typeinfo = TypeInfo.objects.get(pk=int(tid)) #根据主键查找当前的商品分类 海鲜或者水果 news = typeinfo.goodsinfo_set.order_by('-id')[0:2] #list.html左侧最新商品推荐 goods_list = [] # list中间栏商品显示方式 ':#默认最新 goods_list = GoodsInfo.objects.filter(gtype_id=int(tid)).order_by('-id') ':#按照价格 goods_list = GoodsInfo.objects.filter(gtype_id=int(tid)).order_by('-gprice') ':#按照人气点击量 goods_list = GoodsInfo.objects.filter(gtype_id=int(tid)).order_by('-gclick') #创建Paginator一个分页对象 paginator = Paginator(goods_list, 4) #返回Page对象,包含商品信息 page = paginator.page(int(pindex)) context = { 'title': '商品列表', 'guest_cart': 1, 'page': page, 'paginator': paginator, 'typeinfo': typeinfo, 'sort': sort, # 排序方式 'news': news, } return render(request,'df_goods/list.html',context) def detail(request, id): goods = GoodsInfo.objects.get(pk=int(id)) goods.gclick = goods.gclick+1#商品点击量 goods.save() news = goods.gtype.goodsinfo_set.order_by('-id')[0:2] context = { 'title': goods.gtype.ttitle, 'guest_cart': 1, 'goods': goods, 'news': news, 'id':id, } response=render(request, 'df_goods/detail.html', context) #使用列表 记录最近浏览, 在用户中心使用 # goods_ids = request.COOKIES.get('goods_ids', '')#在cookie中建立一个商品id的对应最近浏览的商品 # goods_id = '%d' %goods.id#将url转化为整型 # if goods_ids != '':#判断是否存在浏览记录,如果存在则继续判断, # goods_ids1 = goods_ids.split(',')#拆分为列表 # if goods_ids1.count(goods_id)>=1:#如果商品已经存在记录则删除旧纪录 # goods_ids1.remove(goods_id) # goods_ids1.insert(0, goods_id)#将商品插入到第一页 # if len(goods_ids1)>=6:#每页只显示五个最近浏览的商品 # del goods_ids1[5] # goods_ids = ','.join(goods_ids1)#将商品id拼接为字符串 # else: # goods_ids = goods_id#显然第一次查看detail页面时为空,则直接添加 # response.set_cookie('goods_ids', goods_ids)#写入cookie # 将用户最近浏览商品记录进第三张表 ''' 1,判断是否有用户登录, 如果没有直接结束 2,判断在当前浏览表中是否存在这个用户, 不存在则创建一个用户浏览记录,并且不用判断是否浏览过 若存在则判断当前用户是否存在一个浏览过当前商品 3,不管有没有浏览过当前商品都要先创建一个商品记录放入表中 如果浏览过则删除前期浏览的商品 若没有则不用删除 4,如果商品记录为五条,则将最后的一条删除 ''' try: user_id = request.session['user_id'] # user_list = GoodsBrowser.objects.filter(user_id=int(user_id)) goods_browser = GoodsBrowser() goods_browser.user_id = int(user_id) goods_browser.good_id = int(id) goods_browser.save() old_user_list = GoodsBrowser.objects.filter(user_id=int(user_id), good_id=int(id)) if len(old_user_list) > 1: GoodsBrowser.objects.filter(good_id=int(id)).first().delete() if len(GoodsBrowser.objects.filter(user_id=int(user_id))) > 5: GoodsBrowser.objects.filter(user_id=int(user_id)).first().delete() except: pass return response def cart_count(request): if request.session.has_key('user_id'): return CartInfo.objects.filter(user_id=request.session['user_id']).count else: return 0 class MySearchView(SearchView): def extra_context(self): context = super(MySearchView, self).extra_context() context['title'] = '搜索' context['guest_cart'] = 1 context['cart_count'] = cart_count(self.request) return context def cookieTest(request): response = HttpResponse() if request.COOKIES.get('binggan'):#判断是否有此cookie cookie = request.COOKIES response.write(cookie['binggan'])#如果有则写入 return response
5、admin.py
from django.contrib import admin from .models import TypeInfo,GoodsInfo # Register your models here. #注册模型类 普通方法 class TypeInfoAdmin(admin.ModelAdmin): list_display = ['id', 'ttitle'] # class GoodsInfoAdmin(admin.ModelAdmin): # list_per_page = 15 # list_display = ['id', 'gtitle', 'gunit','gclick', 'gprice','gpic','gjianjie','gkucun','gcontent','gjianjie'] admin.site.register(TypeInfo, TypeInfoAdmin) # admin.site.register(GoodsInfo, GoodsInfoAdmin) # 装饰器方法 @admin.register(GoodsInfo) class GoodsInfoAdmin(admin.ModelAdmin): list_per_page = 15 list_display = ['id', 'gtitle', 'gunit','gclick', 'gprice','gpic','gjianjie','gkucun','gcontent','gjianjie']
6、search_indexes.py
#!/user/bin/env python # -*- coding: utf-8 -*- from haystack import indexes from .models import * #制定对于某个类的某些数据建立索引 class GoodsInfoIndex(indexes.SearchIndex, indexes.Indexable): text = indexes.CharField(document=True, use_template=True) def get_model(self):#对GoodsInfo模型类进行索引 return GoodsInfo def index_queryset(self, using=None): return self.get_model().objects.all()
全局索引提供一个大的全局搜索功能,也可以通过Django 中的ORM的Q查询实现
这里请参考全局索引:https://www.cnblogs.com/welan/p/9231430.html
四、订单相关功能
app目录如下
df_order - migrations - admin.py - apps.py - models.py - tests.py - urls.py - views.py - __init__.py
1、app.py
from django.apps import AppConfig class DfOrderConfig(AppConfig): name = 'df_order'
2、models.py
from django.db import models # Create your models here. class OrderInfo(models.Model):#大订单 oid = models.CharField(max_length=20, primary_key=True)#订单号 user = models.ForeignKey('df_user.UserInfo', on_delete=models.CASCADE)#确定哪个用户的订单 odate = models.DateTimeField(auto_now=True) oIsPay = models.BooleanField(default=False)#当前订单是否支付,默认为否 ototal = models.DecimalField(max_digits=8, decimal_places=2) oaddress = models.CharField(max_length=150) #虽然订单总价可以由多个商品的单价以及数量求得,但是由于用户订单的总价的大量使用,忽略total的冗余度 #无法实现:真实支付,物流信息 class OrderDetailInfo(models.Model):#大订单中的具体某一商品订单 goods = models.ForeignKey('df_goods.GoodsInfo',on_delete=models.CASCADE)#关联商品信息 order = models.ForeignKey('OrderInfo', on_delete=models.CASCADE)#关联大订单,确定属于某一个大订单中 price = models.DecimalField(max_digits=6, decimal_places=2)#某一类商品订单的价格最高达9999.99 count = models.IntegerField()
3、urls.py
#!/user/bin/env python # -*- coding: utf-8 -*- from django.conf.urls import url from . import views app_name = 'df_order' urlpatterns = [ url(r'^$', views.order), url(r'^push/$', views.order_handle), ]
4、view.py
from django.shortcuts import render,HttpResponse,redirect from df_user import user_decorator from django.db import transaction from django.http import JsonResponse from datetime import datetime from decimal import Decimal from .models import * from df_cart.models import CartInfo from df_user.models import UserInfo # Create your views here. @user_decorator.login def order(request): uid = request.session['user_id'] user = UserInfo.objects.get(id=uid) cart_ids = request.GET.getlist('cart_id') carts = [] total_price = 0 for goods_id in cart_ids: cart = CartInfo.objects.get(id = goods_id) carts.append(cart) total_price = total_price + float(cart.count) * float(cart.goods.gprice) total_price = float('%0.2f'%total_price) trans_cost = 10#运费 total_trans_price = trans_cost + total_price # print(total_trans_price) # import datetime订单提交时间 # value = datetime.datetime.now() # print(value) context = { 'title': '提交订单', 'page_name': 1, 'user':user, 'carts': carts, 'total_price':float('%0.2f'%total_price), 'trans_cost': trans_cost, 'total_trans_price': total_trans_price, # 'value':value } return render(request, 'df_order/place_order.html', context) ''' 事务提交: 这些步骤中,任何一环节一旦出错则全部退回1 1. 创建订单对象 2. 判断商品库存是否充足 3. 创建 订单 详情 ,多个 4,修改商品库存 5. 删除购物车 ''' @user_decorator.login @transaction.atomic()#事务 def order_handle(request): tran_id = transaction.savepoint()#保存事务发生点 cart_ids = request.POST.get('cart_ids')#用户提交的订单购物车,此时cart_ids为字符串,例如'1,2,3,' # print('订单购物车', cart_ids) user_id = request.session['user_id']#获取当前用户的id # print('当前用户', user_id) try: order_info = OrderInfo()#创建一个订单对象 now = datetime.now() order_info.oid = '%s%d'%(now.strftime('%Y%m%d%H%M%S'), user_id)#订单号为订单提交时间和用户id的拼接 order_info.odate = now#订单时间 # print('订单时间', now) order_info.user_id = int(user_id)#订单的用户id order_info.ototal = Decimal(request.POST.get('total'))#从前端获取的订单总价 # print('总价', order_info.ototal) order_info.save()#保存订单 for cart_id in cart_ids.split(','):#逐个对用户提交订单中的每类商品即每一个小购物车 cart = CartInfo.objects.get(pk = cart_id)#从CartInfo表中获取小购物车对象 order_detail = OrderDetailInfo()#大订单中的每一个小商品订单 order_detail.order = order_info#外键关联,小订单与大订单绑定 goods = cart.goods#具体商品 if cart.count <= goods.gkucun:#判断库存是否满足订单,如果满足,修改数据库 goods.gkucun = goods.gkucun - cart.count goods.save() # print(goods.gtitle,'库存', goods.gkucun) order_detail.goods = goods order_detail.price = goods.gprice # print('小订单价格',order_detail.price) order_detail.count = cart.count # print('小订单商品数量', order_detail.count) order_detail.save() cart.delete()#并删除当前购物车 else:#否则,则事务回滚,订单取消 transaction.savepoint_rollback(tran_id) return HttpResponse('库存不足') # return redirect('/cart/') data = { 'ok': 1, } transaction.savepoint_commit(tran_id) except Exception as e: print("%s"%e) print('未完成订单提交') transaction.savepoint_rollback(tran_id)#事务任何一个环节出错,则事务全部取消 # return HttpResponse('订单提交成功') return JsonResponse(data) @user_decorator.login def pay(request): pass
五、购物车相关模块功能
app目录如下
df_cart - migrations - admin.py - apps.py - models.py - test.py - urls.py - views.py - __init__.py
1、app.py
from django.apps import AppConfig class DfCartConfig(AppConfig): name = 'df_cart'
2、models.py
from django.db import models # Create your models here. #当一对多关系时例如生鲜分类对生鲜具体商品, 将关系维护在多的那张表中,即在具体商品表中维护 #当多对多关系,则新建一张表,在再第三张表中维护表关系 #用户表与商品表则将关系维护在购物车表中 #在购物车的逻辑删除与物理删除 选择物理删除, #购物车中的商品不属于重要的信息,可以直接删除 class CartInfo(models.Model): user = models.ForeignKey('df_user.UserInfo', on_delete=models.CASCADE) goods = models.ForeignKey('df_goods.GoodsInfo', on_delete=models.CASCADE) #在同级工程目录下引用外键 count = models.IntegerField()#记录用户买个多少单位的商品
3、urls.py
#!/user/bin/env python # -*- coding: utf-8 -*- from django.conf.urls import url from . import views app_name = 'df_cart' urlpatterns = [ url(r'^$', views.cart), url(r'^add(\d+)_(\d+)/$', views.add), url(r'^edit(\d+)_(\d+)/$', views.edit), url(r'^delete(\d+)/$', views.delete), ]
4、view.py
from django.shortcuts import render,redirect from django.http import JsonResponse from df_user import user_decorator from .models import * # Create your views here. @user_decorator.login def cart(request): uid = request.session['user_id'] carts = CartInfo.objects.filter(user_id=uid) context = { 'title':'购物车', 'page_name':1, 'carts':carts } if request.is_ajax(): count = CartInfo.objects.filter(user_id=request.session['user_id']).count() #求当前用户购买了几件商品 return JsonResponse({'count': count}) else: return render(request, 'df_cart/cart.html', context) @user_decorator.login def add(request, gid, count): uid = request.session['user_id'] gid = int(gid) count = int(count) print(gid, count) #查询购物车中是否已经有此商品,如果有则数量增加,如果没有则新增 carts = CartInfo.objects.filter(user_id=uid, goods_id=gid) if len(carts)>=1: cart = carts[0] cart.count = cart.count + count else: cart = CartInfo() cart.user_id = uid cart.goods_id = gid cart.count = count cart.save() #如果是ajax提交则直接返回json,否则转向购物车 if request.is_ajax(): count = CartInfo.objects.filter(user_id=request.session['user_id']).count() #求当前用户购买了几件商品 return JsonResponse({'count': count}) else: return redirect('/cart/') @user_decorator.login def edit(request, cart_id, count): try: cart = CartInfo.objects.get(pk=int(cart_id)) cart.count=int(count) cart.save() data = {'count':0} except Exception as e: data = {'count':count} return JsonResponse(data) @user_decorator.login def delete(request,cart_id): print(cart_id) try: cart = CartInfo.objects.get(pk=int(cart_id)) cart.delete() data={'ok':1} print('数据库修改成功') except Exception as e: data = {'ok':0} return JsonResponse(data)
天天生鲜 - App设计的更多相关文章
- Livecoding.tv 现正举行iOS及Android App设计比赛
近日,Livecoding.tv, 一个为世界各地的程序员提供在线实时交流的平台,在其网站上发布了一篇通知, 宣布从4月15日至5月15日,会为iOS和Android的开发者举办一场本地移动app设计 ...
- 安卓app设计规范整理和Android APP设计篇(转)
随着安卓智能手机不停的更新换代.安卓手机系统越来越完美,屏幕尺寸也越来越大啦!比如最近小米的miui 6的发布和魅族手机系统的更新等等. 以小米MIUI6的安卓手机来说,MIUI6进行了全新设计,坚持 ...
- 最实用的APP界面设计知识,有温度的APP设计(转)
在逛简书的时候,无意之间看到了这样的一篇非常有意思的app设计博文.顾25学堂的摘录了其中的一些关于移动端APP界面设计的精华.分享给25学堂的app设计师们. 当然,下面的这些app设计知识点是来自 ...
- APP设计师拿到APP产品原型开始,七步搞定APP设计(转)
任何一款成功的APP都需要以坚实的产品概念作为基础,因为概念决定了产品最终完成的潜力. 一般情况下,交到app设计师手里的都是移动app产品原型图.当然这个是在移动产品经理反复斟酌,并且与大家开会讨论 ...
- 学习笔记:只有一套app设计稿(5s尺寸)切出4和4s尺寸以及安卓系统主流尺寸的图
如何在只有一套app设计稿(5s尺寸)切出4和4s尺寸以及安卓系统主流尺寸的图 转自:http://www.zhihu.com/question/23255417 版权归原作者所有 目前ios手机 ...
- 2014年的Google I/O app设计中的材料设计-渣渣的翻译
又是一篇翻译,用了三个多小时.http://android-developers.blogspot.co.id/2014/08/material-design-in-2014-google-io-ap ...
- 必胜宅急送Web app设计背后的思考
O2O模式是餐饮业在移动消费趋势下主动拥抱互联网的方向,迎合餐饮消费者从以往经验判断为主转变为依靠移动设备.lbs.社交网络进行立体决策的过程.继App客户端之后,手机web app也逐渐成为O2O中 ...
- “乐”动人心--2017年10款最佳音乐类APP设计盘点
在上下班的路上,听几首自己喜欢的音乐来打发无聊的等公交车和地铁的时间是现代年轻人的常态.音乐作为最能鼓动人心的"语言",也成为了人们在互联网生活里占比例最高的消费活动之一,一款好看 ...
- Django之天天生鲜项目
准备工作 1.配置settings.py内置文件 注意: AUTH_USER_MODEL配置参数要在第一次迁移数据库之前配置,否则可能django的认证系统工作不正常 2.创建应用 3.配置主路由 一 ...
随机推荐
- Dapper实现一对多对象关系聚合导航属性
领域对象:Game(游戏), Room(游戏群),两者一对多的关系,SQL语句中会用到JOIN public class Game : AggregateRoot { public string Ta ...
- vue教程2-05 v-for循环 重复数据无法添加问题 加track-by='索引'
vue教程2-05 v-for循环 重复数据无法添加问题 加track-by='索引' 解决问题的代码示例: <!DOCTYPE html> <html lang="en ...
- 分享一套简单的CodeSmith三层模板
如果要连接mysql,需要安装驱动: https://cdn.mysql.com//Downloads/Connector-Net/mysql-connector-net-8.0.12.msi 连接字 ...
- 什么情况下ArrayList增删 比LinkedList 更快
public static void main(String[] args){ final int MAX_VAL = 10000; List<Integer> linkedList = ...
- vue 运行npm run dev报错
npm run dev运行时报错,原因有很多. 一般用下面这种方法都能解决的. 最简单粗暴的方法: 1.删除依赖包node_modules 2.然后重新npm install就行了 (如果这步报错了, ...
- 前端组件化Polymer深入篇(1)
在前面的几节里面简单的介绍了一下Polymer的基本功能,但还有一些细节的东西并没有讨论,所有打算花点时间把Polymer的一些细节写一下. new和createElement有区别吗? <sc ...
- springboot-32-使用mvc测试
Spring Boot可以和大部分流行的测试框架协同工作:通过Spring JUnit创建单元测试:生成测试数据初始化数据库用于测试:Spring Boot可以跟BDD(Behavier Driven ...
- zk特性和场景
zk解决什么问题 分布式一致性问题 一致性一般定义是分布式系统中状态或数据保持同步和一致.实际上就是围绕着“看见”来的.谁能看见?能否看见?什么时候看见? 举个例子:淘宝后台卖家,在后台上架一件大促的 ...
- springMVC容器和Spring容器
前段时间有人问我,为什么一定要在web.xml中配置spring的listener呢? <listener> <description>spring监听器</descri ...
- Spring 环境与profile(二)——Properties with Spring
1. 简述 Spring profile用例,分3个场景(Test, Dev, Prod)相对Spring 环境与profile(一)——超简用例多了根据具体的profile获取对应的Properti ...