首页
学习
活动
专区
工具
TVP
发布

Django实战教程:开发餐厅在线点评网站(1)

小编我最喜欢写Django的基础知识,尤其是一个一个细小的知识点。因为我相信无论你是新手还是高手,熟练地掌握基础知识才能在实际Web开发项目中游刃有余。然而我们学习Django的最终目的还是应用,今天小编我就带你用Django开发一个餐厅在线点评的APP,也算应读者的要求。如果你对本文中的代码阅读起来还感觉有点吃力,建议关注我的微信公众号,点击经典原创阅读Django基础(1)到(12)。如果对代码有任何问题或不理解,可以在评论区留言。

总体思路

我们要开发一个餐厅点评网站(APP),具体包括以下几个功能性页面。

查看餐厅(restaurants)列表 - 所有用户

查看餐厅详情(包括名称,地址,电话,菜品和点评) - 所有用户

创建餐厅 - 仅限登录用户

修改餐厅 - 仅限登录用户,且每个用户只能修改自己创建的餐厅

给餐厅添加菜品(dishes) - 仅限登录用户

修改菜品信息 - 每个登录用户只能修改自己创建的菜品

查看菜品详情(品名,描述, 图片和价格)

给餐厅添加评论(review)和评分(rating)

如果匿名用户查看餐厅列表和详情时,它们会被要求先登录后再创建餐厅或给餐厅添加评论。我们预期的效果如下图所示。本教程分2部分,本文仅介绍前4个功能性页面。

项目开发环境

Django 2.0 + Python 3.5 + SQLite。因为我们需要上传显示图片,所以请确保你已通过pip安装python的pillow图片库。

项目配置settings.py

我们通过python manage.py startapp myrestaurants创建一个叫myrestaurants的APP,把它加到settings.py里INSATLLED_APP里去,如下所示。users是对Django自带AUTH User的扩展应用。如果不清楚,请阅读

这里

INSTALLED_APPS = [

'django.contrib.admin',

'django.contrib.auth',

'django.contrib.contenttypes',

'django.contrib.sessions',

'django.contrib.staticfiles',

'django.contrib.sites',

'myrestaurants',

'users',

]

因为我们要用到静态文件如css和图片,我们需要在settings.py里设置STATIC_URL和MEDIA。用户上传的图片会放在/media/文件夹里。

STATIC_URL ='/static/'

STATICFILES_DIRS = [os.path.join(BASE_DIR,"static"),]

# specify media root for user uploaded files,

MEDIA_ROOT = os.path.join(BASE_DIR,'media')

MEDIA_URL ='/media/'

整个项目的urls.py如下所示。我们把myrestaurants的urls.py也加进去了。别忘了在结尾部分加static配置。

fromdjango.conf.urlsimporturl,include

fromdjango.contribimportadmin

fromdjango.confimportsettings

fromdjango.conf.urls.staticimportstatic

urlpatterns = [

url(r'^myrestaurants/',include('myrestaurants.urls')),

url(r'^admin/',admin.site.urls),

url(r'^accounts/',include('users.urls')),

] + static(settings.MEDIA_URL,document_root=settings.MEDIA_ROOT)

模型models.py

对于Django而言,能设计一个良好的models,我们已经成功了一半。在本例中我们创建了Restuarant, Dish和Review的模型。因为我们在视图中会应用Django的通用视图,所以我们的模型里还需要定义get_abosolute_url。Django的CreateView和UpdateView在完成对象的创建或编辑后会自动跳转到这个绝对url。

fromdjango.dbimportmodels

fromdjango.contrib.auth.modelsimportUser

fromdatetimeimportdate

fromdjango.urlsimportreverse

classRestaurant(models.Model):

name = models.TextField()

address = models.TextField(blank=True,default='')

telephone = models.TextField(blank=True,default='')

url = models.URLField(blank=True,null=True)

user = models.ForeignKey(User,default=1,on_delete=models.CASCADE)

date = models.DateField(default=date.today)

def__str__(self):

returnself.name

defget_absolute_url(self):

returnreverse('myrestaurants:restaurant_detail',args=[str(self.id)])

classDish(models.Model):

name = models.TextField()

description = models.TextField(blank=True,default='')

price = models.DecimalField('USD amount',max_digits=8,decimal_places=2,blank=True,null=True)

user = models.ForeignKey(User,default=1,on_delete=models.CASCADE)

date = models.DateField(default=date.today)

image = models.ImageField(upload_to="myrestaurants",blank=True,null=True)

restaurant = models.ForeignKey(Restaurant,null=True,related_name='dishes',on_delete=models.CASCADE)

def__str__(self):

returnself.name

defget_absolute_url(self):

returnreverse('myrestaurants:dish_detail',args=[str(self.restaurant.id),str(self.id)])

# This Abstract Review can be used to create RestaurantReview and DishReview

classReview(models.Model):

RATING_CHOICES = ((1,'one'),(2,'two'),(3,'three'),(4,'four'),(5,'five'))

rating = models.PositiveSmallIntegerField('Rating (stars)',blank=False,default=3,choices=RATING_CHOICES)

comment = models.TextField(blank=True,null=True)

user = models.ForeignKey(User,default=1,on_delete=models.CASCADE)

date = models.DateField(default=date.today)

classMeta:

abstract =True

classRestaurantReview(Review):

restaurant = models.ForeignKey(Restaurant,on_delete=models.CASCADE,related_name="reviews")

def__str__(self):

return"{} review".format(self.restaurant.name)

URLConf配置urls.py

每个path都对应一个视图,一个命名的url和我们本文刚开始介绍的一个功能性页面。本文中包含了4个urls。我们在查看餐厅详情和编辑餐厅信息的url中传递了pk(餐厅id)作为参数。

fromdjango.urlsimportpath,re_path

from.importviews

# namespace

app_name ='myrestaurants'

urlpatterns = [

# 查看餐厅列表

path('',views.RestaurantList.as_view(),name='restaurant_list'),

# 查看餐厅详情, 如/myrestaurants/restaurant/1/

re_path(r'^restaurant/(?P

\d+)/$',

views.RestaurantDetail.as_view(),name='restaurant_detail'),

# 创建餐厅, 如:/myrestaurants/restaurant/create/

re_path(r'^restaurant/create/$',views.RestaurantCreate.as_view(),name='restaurant_create'),

# 编辑餐厅详情, 如: /myrestaurants/restaurant/1/edit/

re_path(r'^restaurant/(?P

\d+)/edit/$',

views.RestaurantEdit.as_view(),name='restaurant_edit'),

]

视图views.py

为了简化开发,本例中使用了Django自带的通用视图。我们使用ListView来显示餐厅列表,使用DetailView来显示餐厅详情,使用CreateView来创建餐厅,使用UpdateView来编辑餐厅信息。如果你对通用视图不了解,请阅读下文:

# Create your views here.

fromdjango.httpimportHttpResponseRedirect

fromdjango.shortcutsimportget_object_or_404,render

fromdjango.urlsimportreverse

fromdjango.views.genericimportDetailView,ListView,UpdateView

fromdjango.views.generic.editimportCreateView

from.modelsimportRestaurantReview,Restaurant,Dish

from.formsimportRestaurantForm,DishForm

classRestaurantList(ListView):

queryset = Restaurant.objects.all().order_by('-date')

context_object_name ='latest_restaurant_list'

template_name ='myrestaurants/restaurant_list.html'

classRestaurantDetail(DetailView):

model = Restaurant

template_name ='myrestaurants/restaurant_detail.html'

defget_context_data(self,**kwargs):

context =super(RestaurantDetail,self).get_context_data(**kwargs)

context['RATING_CHOICES'] = RestaurantReview.RATING_CHOICES

returncontext

classRestaurantCreate(CreateView):

model = Restaurant

template_name ='myrestaurants/form.html'

form_class = RestaurantForm

# Associate form.instance.user with self.request.user

defform_valid(self,form):

form.instance.user =self.request.user

returnsuper(RestaurantCreate,self).form_valid(form)

classRestaurantEdit(UpdateView):

model = Restaurant

template_name ='myrestaurants/form.html'

form_class = RestaurantForm

重要知识点:

在RestaurantDetail视图里,我们通过get_context_data方法向模板传递了额外的变量RATING_CHOICES。DetailView视图会接受url传递来的pk值,并显示该模型的所有信息。

在RestaurantCreate视图里,我们使用了form_valid方法。form_valid方法作用是添加前端表单字段以外的信息。在用户在创建餐厅时,我们不希望用户能更改创建用户,于是在前端表单里把user故意除外了(见forms.py),而选择在后台添加user信息。

表单forms.py

创建和编辑对象时需要用到表单,我们的表单如下所示。我们在前端表单里移除了user,而采用后台添加的方式。我们添加了widget和labels。添加widget的目的时为了定制用户输入控件(比如URLInput),并给其添加css样式(因为boostrap表单需要form-control这个样式)。

fromdjango.formsimportModelForm,TextInput,URLInput,ClearableFileInput

from.modelsimportRestaurant,Dish

classRestaurantForm(ModelForm):

classMeta:

model = Restaurant

exclude = ('user','date',)

widgets = {

'name': TextInput(attrs={'class':'form-control'}),

'address': TextInput(attrs={'class':'form-control'}),

'telephone': TextInput(attrs={'class':'form-control'}),

'url': URLInput(attrs={'class':'form-control'}),

}

labels = {

'name':'名称',

'address':'地址',

'telephone':'电话',

'url':'网站',

}

模板文件

我们在目录中创建templates/myrestaurants/目录,添加如下html模板。

# restaurant_list.html

{% extends "myrestaurants/base.html" %}

{% block content %}

餐厅列表

{% for restaurant in latest_restaurant_list %}

{{ restaurant.name }}

{% empty %}

对不起,没有餐厅点评。

{% endfor %}

{% if request.user.is_authenticated %}

class="glyphicon glyphicon-plus"> 添加餐厅

{% else %}

请登录后添加餐厅。

{% endif %}

{% endblock %}

重要知识点:

请观察我们是如何把参数(如餐厅id)传递给命名url的。

请观察我们是如何设置next实现匿名用户登录后立即跳转到餐厅创建页面的。

# restaurant_detail.html

{% extends "myrestaurants/base.html" %}

{% block content %}

{{ restaurant.name }}

{% if request.user == restaurant.user %}

(修改)

{% endif %}

地址

{{ restaurant.address }},

{{ restaurant.telephone }}

菜单

{% if request.user.is_authenticated %}

(添加)

{% endif %}

{% for dish in restaurant.dishes.all %}

{{ dish.name }}- {{ dish.price }}元

{% empty %}

对不起,该餐厅还没有菜肴。

{% endfor %}

用户点评

{% if restaurant.reviews.all %}

{% for review in restaurant.reviews.all %}

{{ review.rating}}星, {{ review.user }}点评, {{ review.date | date:"Y-m-d" }}

{{ review.comment }}

{% endfor %}

{% else %}

目前还没有用户点评。

{% endif %}

添加点评

{% if request.user.is_authenticated %}

{% csrf_token %}

评论

评分

{% for rate in RATING_CHOICES %}

{{ rate.0 }}星

{% endfor %}

{% else %}

请先登录再评论。

{% endif %}

{% endblock %}

重要知识点:

请观察我们是如何通过 if request.user == restaurant.user 来控制每个用户只能编辑自己创建的餐厅的。

# form.html

注意: 创建餐厅和编辑餐厅,我们使用了同样一个模板。

{% extends "myrestaurants/base.html" %}

{% block content %}

{% csrf_token %}

{% for hidden_field in form.hidden_fields %}

{{ hidden_field }}

{% endfor %}

{% if form.non_field_errors %}

{% for error in form.non_field_errors %}

{{ error }}

{% endfor %}

{% endif %}

{% for field in form.visible_fields %}

{{ field.label_tag }}

{{ field }}

{% if field.help_text %}

{{ field.help_text }}

{% endif %}

{% endfor %}

{% endblock %}

# base.html

这个文件基本上只包括样式,不包括逻辑,可以不看。

{% load static %}

{% block title %} Django餐厅点评系统{% endblock %}

class="icon-bar">

class="icon-bar">

class="icon-bar">

Django餐厅点评APP

{% if user.is_authenticated %}

class="glyphicon glyphicon-user">{{ user.username }}class="caret">

我的账户

退出登录

{% else %}

class="glyphicon glyphicon-user">注册

class="glyphicon glyphicon-log-in">登录

{% endif %}

{% block content %}

{% if error_message %}

{{ error_message}}

{% endif %}

{% endblock %}

{% block footer %}{% endblock %}

查看效果

连续运行python manage.py makemigrations, python manage.py migrate和python manage.py runserver, 打开http://127.0.0.1:8000/myrestaurants/就可以看到以下效果了。

餐厅列表(用户登录后跳转到添加餐厅页面)

创建餐厅(用户创建餐厅后跳转到餐厅详情)

餐厅详情页面(用户登录后才能点评)

用户登录后可以修改餐厅信息或提交点评

修改餐厅详情(每个登录用户只能修改自己创建的餐厅)

小结

我们利用Django开发了一个简单的餐厅点评网站,实现了以下4个标黄的功能性页面。下篇教程中,我们将开发剩余4个功能性页面,欢迎关注我的微信公众号获取更多更新。

查看餐厅(restaurants)列表

查看餐厅详情(包括名称,地址,电话,菜品和点评)

创建餐厅 - 仅限登录用户

修改餐厅 - 每个登录用户只能修改自己创建的餐厅

给餐厅添加菜品(dishes) - 仅限登录用户

修改菜品信息 - 每个登录用户只能修改自己创建的菜品

查看菜品详情(品名,描述, 图片和价格)

给餐厅添加评论(review)和评分(rating)

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20180813G0K1T700?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

相关快讯

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券