【Python编程:从入门到实践】第十八章和第十九章练习题(Django创建网站)

18-2  简短的条目 :当前, Django 在管理网站或 shell 中显示 Entry 实例时,模型Entry 的方法 __str__() 都在它的末尾加上省略号。请在方法 __str__() 中添加一条 if 语句,以便仅在条目长度超过 50 字符时才添加省略号。使用管理网站来添加一个长度少于 50 字符的条目,并核实显示它时没有省略号。
18-4  比萨店 :新建一个名为 pizzeria 的项目,并在其中添加一个名为 pizzas 的应用程序。定义一个名为 Pizza 的模型,它包含字段 name ,用于存储比萨名称,如 Hawaiian 和 Meat Lovers 。定义一个名为 Topping 的模型,它包含字段 pizza和 name ,其中字段 pizza 是一个关联到 Pizza 的外键,而字段 name 用于存储配料,如 pineapple 、 Canadian bacon 和 sausage 。
向管理网站注册这两个模型,并使用管理网站输入一些比萨名和配料。使用 shell 来查看你输入的数据。
18-6  比萨店主页 :在你为完成练习 18-4 而创建的项目 Pizzeria 中,添加一个主页。
18-8  比萨店页面 :在练习 18-6 中开发的项目 Pizzeria 中添加一个页面,它显示供应的比萨的名称。然后,将每个比萨名称都设置成一个链接,单击这种链接将显示一个页面,其中列出了相应比萨的配料。请务必使用模板继承来高效地创建页面。

19-1 博客 :新建一个 Django 项目,将其命名为 Blog 。在这个项目中,创建一个名为 blogs 的应用程序,并在其中创建一个名为 BlogPost 的模型。这个模型应包含 title 、 text 和 date_added 等字段。为这个项目创建一个超级用户,并使用管理网站创建几个简短的帖子。创建一个主页,在其中按时间顺序显示所有的帖子。
创建两个表单,其中一个用于发布新帖子,另一个用于编辑既有的帖子。
尝试填写这些表单,确认它们能够正确地工作。
19-2 博客账户 :在你为完成练习 19-1 而开发的项目 Blog 中,添加一个用户身份验证和注册系统。让已登录的用户在屏幕上看到其用户
名,并让未注册的用户看到一个到注册页面的链接。
19-3 重构 :在 views.py 中,我们在两个地方核实主题关联到的用户为当前登录的用户。请将执行这种检查的代码放在一个名为 check_topic_owner() 的函数中,并在恰当的地方调用这个函数。
19-4 保护页面 new_entry :一个用户可在另一个用户的学习笔记中添加条目,方法是输入这样的 URL ,即其中包含输入另一个用户的主题的 ID 。为防范这种攻击,请在保存新条目前,核实它所属的主题归当前用户所有。
19-5 受保护的博客 :在你创建的项目 Blog 中,确保每篇博文都与特定用户相关联。确保任何用户都可访问所有的博文,但只有已登录的用户能够发表博文以及编辑既有博文。在让用户能够编辑其博文的视图中,在处理表单前确认用户编辑的是他自己发表的博文。

合并为项目“pizzeria ”,实现过程(Windows系统+python3.8.0+django3.0.1):

一.创建项目

  1. 新建项目目录“pizzeria”后,终端切换到这个目录下,执行命令“python -m venv ll_env”创建一个虚拟环境(虚拟环境是系统的一个位置,可以在其中安装工程包,并将其与其他 Python 包隔离,将项目的库与其他项目分离是有益的。);
  2. 执行命令“source ll_env/Scripts/activate”激活虚拟环境;
  3. 激活虚拟环境后,执行命令“pip install Django”来安装 Django:

     

  4. 用Django自带的脚手架工具“django-admin.py startproject pizzeria .”创建项目(末尾的句点让新项目使用合适的目录结构,这样开发完成后可轻松地将应用程序部署到服务器);
  5. 执行命令“python manage.py migrate”创建数据库:

     

二.创建应用程序

  1. 执行命令“python manage.py startapp pizzas”创建应用程序;

     

  2. 定义模型“Pizza ”和“Topping”(pizzas--models.py):
    from django.db import models
    
    # Create your models here.
    class Pizza(models.Model):
    	""" 披萨 """
    	# 	由字符或文本组成的数据(限制长度100)
    	name = models.CharField(max_length=100)
    	# 	记录日期和时间的数据(自动创建当前时间)
    	date_added = models.DateTimeField(auto_now_add=True)
    	def __str__(self):
    		return self.name
    class Topping(models.Model):
    	""" 配料 """
    	# 	外键实例
    	# 	django 升级到2.0之后,表与表之间关联的时候,必须要写on_delete参数
    	# 	由于多对多(ManyToManyField)没有 on_delete 参数,所以以上只针对外键(ForeignKey)和一对一(OneToOneField)
    	pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE)
    	name = models.TextField()
    	date_added = models.DateTimeField(auto_now_add=True)
    	def __str__(self):
    		""" 返回模型的字符串表示 """
    		# 	长于50字符才截取和显示省略号
    		if len(self.name) > 50:
    			return self.name[:50] + "..."
    		else:
    			return self.name

     

  3. 要使用模型,必须让 Django 将应用程序包含到项目中(pizzeria--settings.py):

     

  4. 执行命令“python manage.py makemigrations”创建迁移文件,然后执行命令“python manage.py migrate”进行数据库迁移(每次数据的修改,都需要执行以下三个步骤:修改models.py---创建迁移文件---进行数据库迁移):

     

  5. Git Bash执行命令“python manage.py createsuperuser”(创建超级用户)会报错“Superuser creation skipped due to not running in a TTY. You can run `manage.py c reatesuperuser` in your project to create one manually.”;需改成cmd执行,执行之前先激活虚拟环境“ll_env\Scripts\activate”,然后再执行“python manage.py createsuperuser”创建用户:

     

  6. 进行模型注册(pizzas--admin.py):
    from django.contrib import admin
    from pizzas.models import Pizza, Topping
    
    # Register your models here.
    admin.site.register(Pizza)
    admin.site.register(Topping)

     

  7. 访问“http://127.0.0.1:8001/admin/login/”,并输入你创建的用户名和密码进行登录,首页显示如下:

     

三.创建网页(首页和其他页面)

  1. pizzas路由表接入到全局路由表(pizzeria--urls.py)并实现应用pizzas的路由表(文件夹 pizzas 中创建另一个 urls.py 文件):
    # 导入项目和网站管理 URL 的函数和模块
    from django.conf.urls import include, url
    from django.contrib import admin
    urlpatterns = [
    	url(r'^admin/', admin.site.urls),
    	# namespace 将 pizzas 的 URL 同项目中的其他 URL 区分开来
    	# 	源码函数def include(arg, namespace=None)中“urlconf_module, app_name = arg”,所以arg需要传入一个两元元组
    	url(r'', include(('pizzas.urls', 'pizzas'), namespace='pizzas')),
    ]

     

    """ 定义 pizzas 的 URL 模式 """
    from django.conf.urls import url
    from . import views
    urlpatterns = [
        # 主页
        #    r让Python将接下来的字符串视为原始字符串,而引号告诉 Python 正则表达式始于和终于何处。脱字符( ^ )让 Python 查看字符串的开头,
        #    而美元符号让 Python 查看字符串的末尾。总体而言,这个正则表达式让 Python 查找开头和末尾之间没有任何东西的 URL 。
        url(r'^$', views.index, name='index'),
        # 显示所有的配料
        url(r'^pizzas/$', views.pizzas, name='pizzas'),
        # 特定主题的详细页面
        #    r 让 Django 将这个字符串视为原始字符串,并指出正则表达式包含在引号内。
        #    这个表达式的第二部分( /(?P<pizza_id>\d+)/ )与包含在两个斜杠内的整数匹配,并将这个整数存储在一个名为 pizza_id 的实参中。
        #    这部分表达式两边的括号捕获 URL 中的值; ?P<pizza_id> 将匹配的值存储到 pizza_id 中;
        #    而表达式 \d+ 与包含在两个斜杆内的任何数字都匹配,不管这个数字为多少位。
        url(r'^pizza/(?P<pizza_id>\d+)/$', views.pizza, name='pizza'),
    ]

     

     

  2. 编写视图(pizzas--views.py):
    from django.shortcuts import render
    from .models import Pizza
    
    # Create your views here.
    def index(request):
    	""" 披萨的主页 """
    	return render(request, 'pizzas/index.html')
    
    def pizzas(request):
    	""" 显示所有的配料 """
    	pizzas = Pizza.objects.order_by('date_added')
    	context = {'pizzas': pizzas}
    	return render(request, 'pizzas/pizzas.html', context)
    
    def pizza(request, pizza_id):
    	""" 显示单个披萨及其所有的配料 """
    	pizza = Pizza.objects.get(id=pizza_id)
    	# 	date_added 前面的减号指定按降序排列
    	toppings = pizza.topping_set.order_by('-date_added')
    	context = {'pizza': pizza, 'toppings': toppings}
    	return render(request, 'pizzas/pizza.html', context)

     

  3. 在文件夹 pizzas 中新建文件夹 templates--> 新建文件夹pizzas,为了使用模板继承来高效地创建页面,在末级pizzas新建文件base.html,编写代码如下:
    <p>
        <a href="{% url 'pizzas:index' %}">pizzeria</a> -
        <a href="{% url 'pizzas:pizzas' %}">pizzas</a>
    </p>
    {% block content %}{% endblock content %}

     

  4. 在末级pizzas新建文件index.html(图1)、pizzas.html(图2)和pizza.html(图3),编写代码如下:
    {% extends "pizzas/base.html" %}
    {% block content %}
      <p>Pizza, also known as Pizza, Pizza, Pizza, Pizza, is a food originated in Italy, is popular around the world.Pizza is usually made from fermented pita covered with tomato sauce, cheese and other ingredients and baked in an oven.</p>
    {% endblock content %}
    {% extends "pizzas/base.html" %}
    {% block content %}
    <p>Pizzas</p>
    <ul>
        {% for pizza in pizzas %}
        <li>
            <a href="{% url 'pizzas:pizza' pizza.id %}">{{ pizza }}</a>
        </li>
        {% empty %}
        <li>No pizzas have been added yet.</li>
        {% endfor %}
    </ul>
    {% endblock content %}
    {% extends 'pizzas/base.html' %}
    {% block content %}
    <p>Pizza: {{ pizza }}</p>
    <p>Toppings:</p>
    <ul>
        {% for topping in toppings %}
        <li>
            <!-- 竖线( | )表示模板过滤器 -->
            <p>{{ topping.date_added|date:'M d, Y H:i' }}</p>
            <p>{{ topping.name|linebreaks }}</p>
        </li>
        {% empty %}
        <li>
            There are no toppings for this pizza yet.
        </li>
        {% endfor %}
    </ul>
    {% endblock content %}

     

四.增加编辑披萨和配料功能

  1. 添加披萨和配料的表单(forms.py):
    from django import forms
    from .models import Pizza, Topping
    class PizzaForm(forms.ModelForm):
    	class Meta:
    		model = Pizza
    		fields = ['name']
    		# 让 Django 不要为字段 name 生成标签
    		labels = {'name': ''}
    
    class ToppingForm(forms.ModelForm):
    	class Meta:
    		model = Topping
    		fields = ['name']
    		labels = {'name': ''}
    		# 设置属性widgets,可覆盖Django选择的默认小部件
    		# 文本区域改成80列(非默认的40列)
    		widgets = {'name': forms.Textarea(attrs={'cols': 80})}

     

  2. 应用pizzas的路由表增加新路由(后面三个):
    """ 定义 pizzas 的 URL 模式 """
    from django.conf.urls import url
    from . import views
    urlpatterns = [
    	# 主页
    	# 	 r让Python将接下来的字符串视为原始字符串,而引号告诉 Python 正则表达式始于和终于何处。脱字符( ^ )让 Python 查看字符串的开头,
    	# 	 而美元符号让 Python 查看字符串的末尾。总体而言,这个正则表达式让 Python 查找开头和末尾之间没有任何东西的 URL 。
    	url(r'^$', views.index, name='index'),
    	# 显示所有的配料
    	url(r'^pizzas/$', views.pizzas, name='pizzas'),
    	# 特定主题的详细页面
    	#    r 让 Django 将这个字符串视为原始字符串,并指出正则表达式包含在引号内。
    	#    这个表达式的第二部分( /(?P<pizza_id>\d+)/ )与包含在两个斜杠内的整数匹配,并将这个整数存储在一个名为 pizza_id 的实参中。
    	#    这部分表达式两边的括号捕获 URL 中的值; ?P<pizza_id> 将匹配的值存储到 pizza_id 中;
    	#    而表达式 \d+ 与包含在两个斜杆内的任何数字都匹配,不管这个数字为多少位。
    	url(r'^pizza/(?P<pizza_id>\d+)/$', views.pizza, name='pizza'),
    	# 用于添加新披萨的页面
    	url(r'^new_pizza/$', views.new_pizza, name='new_pizza'),
    	# 用于添加新配料的页面
    	url(r'^new_topping/(?P<pizza_id>\d+)/$', views.new_topping, name='new_topping'),
    	# 用于编辑配料的页面
    	url(r'^edit_topping/(?P<topping_id>\d+)/$', views.edit_topping, name='edit_topping'),
    ]

     

  3. 修改视图(pizzas--views.py):
    from django.shortcuts import render
    from django.http import HttpResponseRedirect
    
    # django2.0后把原来的django.core.urlresolvers包更改为了django.urls包
    from django.urls import reverse
    from .models import Pizza, Topping
    from .forms import PizzaForm, ToppingForm
    
    # Create your views here.
    def index(request):
    	""" 披萨的主页 """
    	return render(request, 'pizzas/index.html')
    
    def pizzas(request):
    	""" 显示所有的配料 """
    	pizzas = Pizza.objects.order_by('date_added')
    	context = {'pizzas': pizzas}
    	return render(request, 'pizzas/pizzas.html', context)
    
    def pizza(request, pizza_id):
    	""" 显示单个披萨及其所有的配料 """
    	pizza = Pizza.objects.get(id=pizza_id)
    	# 	date_added 前面的减号指定按降序排列
    	toppings = pizza.topping_set.order_by('-date_added')
    	context = {'pizza': pizza, 'toppings': toppings}
    	return render(request, 'pizzas/pizza.html', context)
    
    def new_pizza(request):
    	""" 添加新披萨 """
    	if request.method != 'POST':
    		#  未提交数据:创建一个新表单
    		form = PizzaForm()
    	else:
    		# POST 提交的数据 , 对数据进行处理
    		form = PizzaForm(request.POST)
    		# is_valid校验填写了必填信息和符合数据类型及长度
    		if form.is_valid():
    			form.save()
    			# 将用户重定向到网页 pizzas
    			return HttpResponseRedirect(reverse('pizzas:pizzas'))
    	context = {'form': form}
    	return render(request, 'pizzas/new_pizza.html', context)
    
    def new_topping(request, pizza_id):
    	""" 在特定的披萨中添加新配料 """
    	pizza = Pizza.objects.get(id=pizza_id)
    	if request.method != 'POST':
    		#  未提交数据 , 创建一个空表单
    		form = ToppingForm()
    	else:
    		# POST 提交的数据 , 对数据进行处理
    		form = ToppingForm(data=request.POST)
    		if form.is_valid():
    			# 传递了实参commit=False,让Django创建一个新的配料对象,并将其存储到new_topping中,但不将它保存到数据库中
    			new_topping = form.save(commit=False)
    			new_topping.pizza = pizza
    			# 把配料保存到数据库,并将其与正确的披萨相关联
    			new_topping.save()
    			return HttpResponseRedirect(reverse('pizzas:pizza', args=[pizza_id]))
    	context = {'pizza': pizza, 'form': form}
    	return render(request, 'pizzas/new_topping.html', context)
    
    def edit_topping(request, topping_id):
    	""" 编辑既有配料 """
    	topping = Topping.objects.get(id=topping_id)
    	pizza = topping.pizza
    	if request.method != 'POST':
    		#  初次请求,使用当前条目填充表单(instance=topping)
    		form = ToppingForm(instance=topping)
    	else:
    		# POST 提交的数据,对数据进行处理
    		form = ToppingForm(instance=topping, data=request.POST)
    		if form.is_valid():
    			form.save()
    			return HttpResponseRedirect(reverse('pizzas:pizza', args=[pizza.id]))
    	context = {'topping': topping, 'pizza': pizza, 'form': form}
    	return render(request, 'pizzas/edit_topping.html', context)

     

  4. 新建模板new_pizza.html(图1)、new_topping.html(图2)、edit_topping.html(图3):
    {% extends "pizzas/base.html" %}
    {% block content %}
    <p>Add a new pizza:</p>
    <form action="{% url 'pizzas:new_pizza' %}" method='post'>
    	<!-- 防止攻击者利用表单来获得对服务器未经授权的访问 -->
        {% csrf_token %}
        <!-- 显示表单(段落格式渲染) -->
        {{ form.as_p }}
        <button name="submit">add pizza</button>
    </form>
    {% endblock content %}
    {% extends "pizzas/base.html" %}
    {% block content %}
    <p><a href="{% url 'pizzas:pizza' pizza.id %}">{{ pizza }}</a></p>
    <p>Add a new topping:</p>
    <form action="{% url 'pizzas:new_topping' pizza.id %}" method='post'>
        {% csrf_token %}
        {{ form.as_p }}
        <button name='submit'>add topping</button>
    </form>
    {% endblock content %}
    {% extends "pizzas/base.html" %}
    {% block content %}
    <p><a href="{% url 'pizzas:pizza' pizza.id %}">{{ pizza }}</a></p>
    <p>Edit topping:</p>
    <form action="{% url 'pizzas:edit_topping' topping.id %}" method='post'>
        {% csrf_token %}
        {{ form.as_p }}
        <button name="submit">save changes</button>
    </form>
    {% endblock content %}

     

  5. 修改模板pizzas.html(图1)、pizza.html(图2):
    {% extends "pizzas/base.html" %}
    {% block content %}
    <p>Pizzas</p>
    <ul>
        {% for pizza in pizzas %}
        <li>
            <a href="{% url 'pizzas:pizza' pizza.id %}">{{ pizza }}</a>
        </li>
        {% empty %}
        <li>No pizzas have been added yet.</li>
        {% endfor %}
    </ul>
    <a href="{% url 'pizzas:new_pizza' %}">Add a new pizza:</a>
    {% endblock content %}
    {% extends 'pizzas/base.html' %}
    {% block content %}
    <p>Pizza: {{ pizza }}</p>
    <p>Toppings:</p>
    <p>
        <a href="{% url 'pizzas:new_topping' pizza.id %}">add new topping</a>
    </p>
    <ul>
        {% for topping in toppings %}
        <li>
            <!-- 竖线( | )表示模板过滤器 -->
            <p>{{ topping.date_added|date:'M d, Y H:i' }}</p>
            <p>{{ topping.name|linebreaks }}</p>
            <p>
                <a href="{% url 'pizzas:edit_topping' topping.id %}">edit topping</a>
            </p>
        </li>
        {% empty %}
        <li>
            There are no toppings for this pizza yet.
        </li>
        {% endfor %}
    </ul>
    {% endblock content %}

     

五.创建用户账户(登录、注销和注册)

  1. 执行命令“python manage.py startapp users”创建“users”的应用程序;
  2. 将应用程序 users 添加到 settings.py 中:

     

  3. users路由表接入到全局路由表(pizzeria--urls.py)并实现应用users的路由表(文件夹 users 中创建另一个 urls.py 文件):
    # 导入项目和网站管理 URL 的函数和模块
    from django.conf.urls import include, url
    from django.contrib import admin
    urlpatterns = [
        url(r'^admin/', admin.site.urls),
        url(r'^users/', include(('users.urls', 'users'), namespace='users')),
        # namespace 将 pizzas 的 URL 同项目中的其他 URL 区分开来
        #   源码函数def include(arg, namespace=None)中“urlconf_module, app_name = arg”,所以arg需要传入一个两元元组
        url(r'', include(('pizzas.urls', 'pizzas'), namespace='pizzas')),
    ]
    """ 为应用程序 users 定义 URL 模式 """
    from django.conf.urls import url
    from django.contrib.auth.views import LoginView
    from . import views
    
    urlpatterns = [
        #登录界面  LoginView.as_view后面要加上()
        url(r'^login/$', LoginView.as_view(template_name='users/login.html'), name='login'),
        # 注销
    	url(r'^logout/$', views.logout_view, name='logout'),
    	# 注册页面
    	url(r'^register/$', views.register, name='register'),
    ]

     

  4. 在文件夹 users 中新建文件夹 templates--> 新建文件夹users,在末级users新建文件login.html(图1)和register.html(图2),编写代码如下:
    {% extends "pizzas/base.html" %}
    {% block content %}
        {% if form.errors %}
            <p>Your username and password didn't match. Please try again.</p>
        {% endif %}
            <form method="post" action="{% url 'users:login' %}">
                {% csrf_token %}
                {{ form.as_p }}
                <button name="submit">log in</button>
                <input type="hidden" name="next" value="{% url 'pizzas:index' %}" />
            </form>
    {% endblock content %}
    {% extends "pizzas/base.html" %}
    {% block content %}
        <form method="post" action="{% url 'users:register' %}">
            {% csrf_token %}
            {{ form.as_p }}
            <button name="submit">register</button>
            <input type="hidden" name="next" value="{% url 'pizzas:index' %}" />
        </form>
    {% endblock content %}

     

  5. 修改base.html页面内容:
    <p>
        <a href="{% url 'pizzas:index' %}">pizzeria</a> -
        <a href="{% url 'pizzas:pizzas' %}">pizzas</a> -
        {% if user.is_authenticated %}
            Hello, {{ user.username }}.
            <a href="{% url 'users:logout' %}">log out</a>
        {% else %}
            <a href="{% url 'users:register' %}">register</a> -
            <a href="{% url 'users:login' %}">log in</a>
        {% endif %}
    </p>
    {% block content %}{% endblock content %}

     

  6. 修改视图(users--views.py):
    from django.shortcuts import render
    from django.http import HttpResponseRedirect
    from django.urls  import reverse
    from django.contrib.auth import login, logout, authenticate
    from django.contrib.auth.forms import UserCreationForm
    
    def logout_view(request):
        """ 注销用户 """
        logout(request)
        return HttpResponseRedirect(reverse('pizzas:index'))
    
    def register(request):
        """ 注册新用户 """
        if request.method != 'POST':
            # 显示空的注册表单
            form = UserCreationForm()
        else:
            # 处理填写好的表单
            form = UserCreationForm(data=request.POST)
            if form.is_valid():
                new_user = form.save()
                # 让用户自动登录,再重定向到主页
                authenticated_user = authenticate(username=new_user.username, password=request.POST['password1'])
                login(request, authenticated_user)
                return HttpResponseRedirect(reverse('pizzas:index'))
        context = {'form': form}
        return render(request, 'users/register.html', context)

     

六.让用户拥有自己的数据

  1. 使用 @login_required 限制访问,除了首页、登录页面和注册页面,其它页面都只有已登录的用户才能访问数据;
  2. 只允许用户访问自己的数据,合并1的代码如下(pizzas-views.py):
    from django.shortcuts import render
    from django.http import HttpResponseRedirect, Http404
    
    # django2.0后把原来的django.core.urlresolvers包更改为了django.urls包
    from django.urls import reverse
    from django.contrib.auth.decorators import login_required
    from .models import Pizza, Topping
    from .forms import PizzaForm, ToppingForm
    
    # Create your views here.
    def index(request):
        """ 披萨的主页 """
        return render(request, 'pizzas/index.html')
    
    # login_required() 的代码检查用户是否已登录,仅当用户已登录时, Django 才运行 topics() 的代码。如果用户未登录,就重定向到登录页面。
    @login_required
    def pizzas(request):
        """ 显示所有的配料 """
        pizzas = Pizza.objects.filter(owner=request.user).order_by('date_added')
        context = {'pizzas': pizzas}
        return render(request, 'pizzas/pizzas.html', context)
    
    @login_required
    def pizza(request, pizza_id):
        """ 显示单个披萨及其所有的配料 """
        pizza = Pizza.objects.get(id=pizza_id)
        # 请求的披萨不归当前用户所有,我们就引发 Http404 异常,让 Django 返回一个 404 错误页面
        check_topic_owner(pizza, request)
        #   date_added 前面的减号指定按降序排列
        toppings = pizza.topping_set.order_by('-date_added')
        context = {'pizza': pizza, 'toppings': toppings}
        return render(request, 'pizzas/pizza.html', context)
    
    @login_required
    def new_pizza(request):
        """ 添加新披萨 """
        if request.method != 'POST':
            #  未提交数据:创建一个新表单
            form = PizzaForm()
        else:
            # POST 提交的数据 , 对数据进行处理
            form = PizzaForm(request.POST)
            # is_valid校验填写了必填信息和符合数据类型及长度
            if form.is_valid():
                new_pizza = form.save(commit=False)
                new_pizza.owner = request.user
                new_pizza.save()
                # 将用户重定向到网页 pizzas
                return HttpResponseRedirect(reverse('pizzas:pizzas'))
        context = {'form': form}
        return render(request, 'pizzas/new_pizza.html', context)
    
    @login_required
    def new_topping(request, pizza_id):
        """ 在特定的披萨中添加新配料 """
        pizza = Pizza.objects.get(id=pizza_id)
        # 解决一个用户可在另一个用户的学习笔记中添加条目问题
        check_topic_owner(pizza, request)
    
        if request.method != 'POST':
            #  未提交数据 , 创建一个空表单
            form = ToppingForm()
        else:
            # POST 提交的数据 , 对数据进行处理
            form = ToppingForm(data=request.POST)
            if form.is_valid():
                # 传递了实参commit=False,让Django创建一个新的配料对象,并将其存储到new_topping中,但不将它保存到数据库中
                new_topping = form.save(commit=False)
                new_topping.pizza = pizza
                # 把配料保存到数据库,并将其与正确的披萨相关联
                new_topping.save()
                return HttpResponseRedirect(reverse('pizzas:pizza', args=[pizza_id]))
        context = {'pizza': pizza, 'form': form}
        return render(request, 'pizzas/new_topping.html', context)
    
    @login_required
    def edit_topping(request, topping_id):
        """ 编辑既有配料 """
        topping = Topping.objects.get(id=topping_id)
        pizza = topping.pizza
        check_topic_owner(pizza, request)
        if request.method != 'POST':
            #  初次请求,使用当前条目填充表单(instance=topping)
            form = ToppingForm(instance=topping)
        else:
            # POST 提交的数据,对数据进行处理
            form = ToppingForm(instance=topping, data=request.POST)
            if form.is_valid():
                form.save()
                return HttpResponseRedirect(reverse('pizzas:pizza', args=[pizza.id]))
        context = {'topping': topping, 'pizza': pizza, 'form': form}
        return render(request, 'pizzas/edit_topping.html', context)
    
    def check_topic_owner(pizza, request):
        """校验关联到的用户是否为当前登录的用户"""
        if pizza.owner != request.user:
            raise Http404

     

  3. settings.py末尾添加“LOGIN_URL = '/users/login/'”,使未登录时访问到校验需要登录的页面,重定向到登录页面;
  4. 将数据关联到用户,先修改models.py(图1,pizza增加用户外键),然后查找所有用户ID---使其数据(pizza)关联到超级用户---迁移数据库(图2);
    from django.db import models
    from django.contrib.auth.models import User
    
    
    # Create your models here.
    class Pizza(models.Model):
        """ 披萨 """
        #   由字符或文本组成的数据(限制长度100)
        name = models.CharField(max_length=100)
        #   记录日期和时间的数据(自动创建当前时间)
        date_added = models.DateTimeField(auto_now_add=True)
        owner = models.ForeignKey(User, on_delete=models.CASCADE)
        def __str__(self):
            return self.name
    class Topping(models.Model):
        """ 配料 """
        #   外键实例
        #   django 升级到2.0之后,表与表之间关联的时候,必须要写on_delete参数
        #   由于多对多(ManyToManyField)没有 on_delete 参数,所以以上只针对外键(ForeignKey)和一对一(OneToOneField)
        pizza = models.ForeignKey(Pizza, on_delete=models.CASCADE)
        name = models.TextField()
        date_added = models.DateTimeField(auto_now_add=True)
        def __str__(self):
            """ 返回模型的字符串表示 """
            #   长于50字符才截取和显示省略号
            if len(self.name) > 50:
                return self.name[:50] + "..."
            else:
                return self.name

     

  5. 切换不同用户登录只能查看自己的数据,访问非自己数据的URL提示404错误:

    总结,整个项目结构如下:

     

  • 6
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值