feat: 📘 add recipes to posts
This commit is contained in:
parent
e48e88ead2
commit
6f3ed061b4
17 changed files with 433 additions and 7 deletions
|
|
@ -21,6 +21,14 @@ from django.urls import include, path
|
|||
|
||||
from moku.views.auth import LoginView, LogoutView
|
||||
from moku.views.post import FeedView
|
||||
from moku.views.recipe import (
|
||||
DeleteRecipeView,
|
||||
DeleteStepView,
|
||||
EditStepView,
|
||||
IndexRecipeView,
|
||||
NewRecipeView,
|
||||
ShowRecipeView,
|
||||
)
|
||||
from moku.views.static import ChangelogView, PrivacyView, TermsView
|
||||
from moku.views.user import EditProfileView, EditSettingsView, ProfileView, SignupView
|
||||
|
||||
|
|
@ -36,6 +44,12 @@ urlpatterns = [
|
|||
path("privacy", PrivacyView.as_view(), name="privacy"),
|
||||
path("terms", TermsView.as_view(), name="terms"),
|
||||
path("user/<str:username>", ProfileView.as_view(), name="profile"),
|
||||
path("recipes", IndexRecipeView.as_view(), name="recipe.index"),
|
||||
path("recipes/new", NewRecipeView.as_view(), name="recipe.new"),
|
||||
path("recipes/<str:uuid>", ShowRecipeView.as_view(), name="recipe.show"),
|
||||
path("recipes/<str:uuid>/delete", DeleteRecipeView.as_view(), name="recipe.delete"),
|
||||
path("recipes/<str:uuid>/<str:step>", EditStepView.as_view(), name="step.edit"),
|
||||
path("recipes/<str:uuid>/<str:step>/delete", DeleteStepView.as_view(), name="step.delete"),
|
||||
]
|
||||
|
||||
if settings.DEBUG_TOOLBAR:
|
||||
|
|
|
|||
|
|
@ -7,10 +7,11 @@ from moku.models import Post
|
|||
class PostForm(ModelForm):
|
||||
class Meta:
|
||||
model = Post
|
||||
fields = ("emoji", "verb", "food", "image")
|
||||
fields = ("emoji", "verb", "food", "recipe", "image")
|
||||
labels = {
|
||||
"emoji": _("emoji"),
|
||||
"verb": _("verb"),
|
||||
"food": _("food"),
|
||||
"recipe": _("recipe"),
|
||||
"image": _("image"),
|
||||
}
|
||||
|
|
|
|||
19
moku/forms/recipe.py
Normal file
19
moku/forms/recipe.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
from django.forms import Form, ModelForm
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from moku.models.recipe import Recipe, RecipeStep
|
||||
|
||||
|
||||
class RecipeForm(ModelForm):
|
||||
class Meta:
|
||||
model = Recipe
|
||||
fields = ("title",)
|
||||
labels = {
|
||||
"title": _("recipe title"),
|
||||
}
|
||||
|
||||
|
||||
class RecipeStepForm(ModelForm):
|
||||
class Meta:
|
||||
model = RecipeStep
|
||||
fields = ("instructions",)
|
||||
42
moku/migrations/0006_recipe_post_recipe_recipestep.py
Normal file
42
moku/migrations/0006_recipe_post_recipe_recipestep.py
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Generated by Django 5.0.3 on 2024-03-25 21:09
|
||||
|
||||
import django.db.models.deletion
|
||||
import shortuuid.django_fields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('moku', '0005_usersettings'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Recipe',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('uuid', shortuuid.django_fields.ShortUUIDField(alphabet=None, help_text='the unique id that identifies this recipe.', length=22, max_length=22, prefix='', verbose_name='unique id')),
|
||||
('title', models.CharField(help_text='give the recipe a title, just so you know the recipe is for.', max_length=64, verbose_name='recipe title')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('updated_at', models.DateTimeField(auto_now=True)),
|
||||
('created_by', models.ForeignKey(db_column='created_by_user_id', on_delete=django.db.models.deletion.CASCADE, related_name='recipes', to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='post',
|
||||
name='recipe',
|
||||
field=models.ForeignKey(blank=True, help_text='the recipe for what you ate, if you have one.', null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='+', to='moku.recipe', verbose_name='recipe'),
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='RecipeStep',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('uuid', shortuuid.django_fields.ShortUUIDField(alphabet=None, help_text='the unique id that identifies this step.', length=22, max_length=22, prefix='', verbose_name='step id')),
|
||||
('instructions', models.CharField(help_text='the instructions for this step of the recipe. try to keep it clear and concise!', max_length=128, verbose_name='step instructions')),
|
||||
('order', models.IntegerField(db_index=True, default=0, help_text='which step in the recipe is this. this affects the order the recipe steps are shown.', verbose_name='step number')),
|
||||
('recipe', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='steps', to='moku.recipe')),
|
||||
],
|
||||
),
|
||||
]
|
||||
|
|
@ -1,8 +1,12 @@
|
|||
from moku.models.post import Post
|
||||
from moku.models.user import User
|
||||
from moku.models.recipe import Recipe, RecipeStep
|
||||
from moku.models.user import User, UserSettings
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Post",
|
||||
"Recipe",
|
||||
"RecipeStep",
|
||||
"User",
|
||||
"UserSettings",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from shortuuid.django_fields import ShortUUIDField
|
||||
|
||||
from moku.constants import Verbs
|
||||
from moku.models.recipe import RecipeStep
|
||||
from moku.validators import validate_emoji
|
||||
|
||||
|
||||
|
|
@ -11,6 +12,15 @@ def post_image_filename(instance, _):
|
|||
return f"posts/{instance.created_by.username}__{instance.uuid}.webp"
|
||||
|
||||
|
||||
class PostManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset().select_related("created_by") \
|
||||
.prefetch_related(
|
||||
models.Prefetch("recipe__steps", queryset=RecipeStep.objects.order_by("order"))
|
||||
) \
|
||||
.order_by("-created_at")
|
||||
|
||||
|
||||
class Post(models.Model):
|
||||
uuid = ShortUUIDField(
|
||||
verbose_name=_("unique id"),
|
||||
|
|
@ -41,6 +51,15 @@ class Post(models.Model):
|
|||
upload_to=post_image_filename,
|
||||
help_text=_("here you can upload a picture of what you ate!"),
|
||||
)
|
||||
recipe = models.ForeignKey(
|
||||
"Recipe",
|
||||
verbose_name=_("recipe"),
|
||||
blank=True,
|
||||
null=True,
|
||||
related_name="+",
|
||||
on_delete=models.SET_NULL,
|
||||
help_text=_("the recipe for what you ate, if you have one."),
|
||||
)
|
||||
created_by = models.ForeignKey(
|
||||
"User",
|
||||
related_name="posts",
|
||||
|
|
@ -57,6 +76,8 @@ class Post(models.Model):
|
|||
help_text=_("when this post was last updated."),
|
||||
)
|
||||
|
||||
objects = PostManager()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.text} on {self.created_at}"
|
||||
|
||||
|
|
|
|||
76
moku/models/recipe.py
Normal file
76
moku/models/recipe.py
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.urls import reverse
|
||||
from shortuuid.django_fields import ShortUUIDField
|
||||
|
||||
|
||||
class RecipeManager(models.Manager):
|
||||
def get_queryset(self):
|
||||
return super().get_queryset() \
|
||||
.prefetch_related(
|
||||
models.Prefetch("steps", queryset=RecipeStep.objects.order_by("order"))
|
||||
)
|
||||
|
||||
|
||||
class Recipe(models.Model):
|
||||
uuid = ShortUUIDField(
|
||||
verbose_name=_("unique id"),
|
||||
max_length=22,
|
||||
length=22,
|
||||
help_text=_("the unique id that identifies this recipe."),
|
||||
)
|
||||
title = models.CharField(
|
||||
verbose_name=_("recipe title"),
|
||||
max_length=64,
|
||||
help_text=_("give the recipe a title, just so you know the recipe is for."),
|
||||
)
|
||||
created_by = models.ForeignKey(
|
||||
"User",
|
||||
related_name="recipes",
|
||||
db_index=True,
|
||||
db_column="created_by_user_id",
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
created_at = models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
)
|
||||
updated_at = models.DateTimeField(
|
||||
auto_now=True,
|
||||
)
|
||||
|
||||
objects = RecipeManager()
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.title} by @{self.created_by.username}"
|
||||
|
||||
def get_absolute_url(self):
|
||||
return reverse("recipe.show", kwargs={"uuid": self.uuid})
|
||||
|
||||
|
||||
class RecipeStep(models.Model):
|
||||
uuid = ShortUUIDField(
|
||||
verbose_name=_("step id"),
|
||||
max_length=22,
|
||||
length=22,
|
||||
help_text=_("the unique id that identifies this step."),
|
||||
)
|
||||
instructions = models.CharField(
|
||||
verbose_name=_("step instructions"),
|
||||
max_length=128,
|
||||
help_text=_("the instructions for this step of the recipe. try to keep it clear and concise!"),
|
||||
)
|
||||
order = models.IntegerField(
|
||||
verbose_name=_("step number"),
|
||||
default=0,
|
||||
db_index=True,
|
||||
help_text=_("which step in the recipe is this. this affects the order the recipe steps are shown."),
|
||||
)
|
||||
recipe = models.ForeignKey(
|
||||
"Recipe",
|
||||
related_name="steps",
|
||||
db_index=True,
|
||||
on_delete=models.CASCADE,
|
||||
)
|
||||
|
||||
def __str__(self):
|
||||
return f"step #{self.order + 1} of {self.recipe}"
|
||||
|
|
@ -39,6 +39,14 @@ body {
|
|||
margin-block-end: 1.6rem;
|
||||
}
|
||||
|
||||
.small {
|
||||
font-size: 1.4rem;
|
||||
}
|
||||
|
||||
.subtle {
|
||||
color: var(--charcoal);
|
||||
}
|
||||
|
||||
.messages {
|
||||
margin-block-end: 1.6rem;
|
||||
display: grid;
|
||||
|
|
@ -150,6 +158,12 @@ form .field {
|
|||
row-gap: .4rem;
|
||||
}
|
||||
|
||||
form .field .field-button {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto;
|
||||
column-gap: .8rem;
|
||||
}
|
||||
|
||||
form .checkbox {
|
||||
display: grid;
|
||||
grid-template-columns: auto 1fr;
|
||||
|
|
@ -341,13 +355,21 @@ header nav ul {
|
|||
margin-block-start: 1.2rem;
|
||||
}
|
||||
|
||||
.block ul {
|
||||
.block ul,
|
||||
.block ol {
|
||||
padding-inline-start: 3.2rem;
|
||||
list-style: disc;
|
||||
display: grid;
|
||||
row-gap: .4rem;
|
||||
}
|
||||
|
||||
.block ul {
|
||||
list-style: disc;
|
||||
}
|
||||
|
||||
.block ol {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.block strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@
|
|||
<ul>
|
||||
{% if request.user.is_authenticated %}
|
||||
<li><a href="{{ url('feed') }}">{% trans %}feed{% endtrans %}</a></li>
|
||||
<li><a href="{{ url('profile', username=request.user.username) }}">{% trans %}my profile{% endtrans %}</a></li>
|
||||
<li><a href="{{ url('recipe.index') }}">{% trans %}recipes{% endtrans %}</a></li>
|
||||
<li><a href="{{ url('profile', username=request.user.username) }}">{% trans %}profile{% endtrans %}</a></li>
|
||||
<li><a href="{{ url('settings') }}">{% trans %}settings{% endtrans %}</a></li>
|
||||
<li>
|
||||
<form action="{{ url('logout') }}" method="POST" class="logout">
|
||||
|
|
|
|||
|
|
@ -38,6 +38,11 @@
|
|||
</select>
|
||||
<span class="help" id="help_verb">{{ form.verb.help_text }}</span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="id_recipe">{{ form.recipe.label }}</label>
|
||||
{{ form.recipe }}
|
||||
<span class="help">{{ form.recipe.help_text }}</span>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label for="id_image">{{ form.image.label }}</label>
|
||||
{{ form.image }}
|
||||
|
|
|
|||
16
moku/templates/moku/recipe/edit_step.jinja
Normal file
16
moku/templates/moku/recipe/edit_step.jinja
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{% extends "moku/base.jinja" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content">
|
||||
<form action="" method="POST" class="auth">
|
||||
{% include "moku/snippets/form_errors.jinja" %}
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label for="id_instructions">{% trans %}instructions{% endtrans %}</label>
|
||||
{{ form.instructions }}
|
||||
<span class="help">{{ form.instructions.help_text }}</span>
|
||||
</div>
|
||||
<button type="submit">{% trans %}update!{% endtrans %}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
16
moku/templates/moku/recipe/form.jinja
Normal file
16
moku/templates/moku/recipe/form.jinja
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{% extends "moku/base.jinja" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content">
|
||||
<form action="" method="POST" class="auth">
|
||||
{% include "moku/snippets/form_errors.jinja" %}
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label for="id_title">{{ form.title.label }}</label>
|
||||
{{ form.title }}
|
||||
<span class="help">{{ form.title.help_text }}</span>
|
||||
</div>
|
||||
<button type="submit">{% trans %}create!{% endtrans %}</button>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
16
moku/templates/moku/recipe/index.jinja
Normal file
16
moku/templates/moku/recipe/index.jinja
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{% extends "moku/base.jinja" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content block">
|
||||
<p class="mb">👏 <a href="{{ url('recipe.new') }}">new recipe</a></p>
|
||||
<h2>your recipes</h2>
|
||||
<ul>
|
||||
{% if not recipes %}
|
||||
<li>{% trans %}no recipes yet... 😴{% endtrans %}</li>
|
||||
{% endif %}
|
||||
{% for recipe in recipes %}
|
||||
<li><a href="{{ recipe.get_absolute_url() }}">{{ recipe.title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
43
moku/templates/moku/recipe/show.jinja
Normal file
43
moku/templates/moku/recipe/show.jinja
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
{% extends "moku/base.jinja" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content block">
|
||||
{% if request.user.id == recipe.created_by.id %}
|
||||
<p class="mb">🗑️ <a href="{{ url('recipe.delete', uuid=recipe.uuid) }}">{% trans %}delete recipe{% endtrans %}</a></p>
|
||||
{% endif %}
|
||||
<h2>{{ recipe.title }}</h2>
|
||||
<ol>
|
||||
{% set count = recipe.steps.count() %}
|
||||
{% for step in recipe.steps.all() %}
|
||||
<li>
|
||||
{{ step.instructions }}
|
||||
{% if request.user.is_authenticated and request.user.id == recipe.created_by.id %}
|
||||
<span class="small subtle">
|
||||
[<a href="{{ url('step.edit', uuid=recipe.uuid, step=step.uuid) }}">{% trans %}edit{% endtrans %}</a>]
|
||||
{% if loop.index0 == (count-1) %}
|
||||
[<a href="{{ url('step.delete', uuid=recipe.uuid, step=step.uuid) }}">{% trans %}delete{% endtrans %}</a>]
|
||||
{% endif %}
|
||||
</span>
|
||||
{% endif %}
|
||||
</li>
|
||||
{% endfor %}
|
||||
{% if request.user.is_authenticated and request.user.id == recipe.created_by.id %}
|
||||
{% if count < 16 %}
|
||||
<li>
|
||||
<form action="" method="POST">
|
||||
{% csrf_token %}
|
||||
<div class="field">
|
||||
<label for="id_instructions">👉 <strong>{% trans %}add a step{% endtrans %}</strong></label>
|
||||
<div class="field-button">
|
||||
{{ form.instructions }}
|
||||
<button type="submit">{% trans %}add!{% endtrans %}</button>
|
||||
</div>
|
||||
<span class="help">{{ form.instructions.help_text }}</span>
|
||||
</div>
|
||||
</form>
|
||||
</li>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
</ol>
|
||||
</div>
|
||||
{% endblock content %}
|
||||
|
|
@ -10,6 +10,15 @@
|
|||
<div class="body">
|
||||
<p class="food">{{ post.text|safe }}</p>
|
||||
<p class="metadata">{{ post.created_at|naturaltime }}</p>
|
||||
<!-- <details class="recipe"><summary>recipe</summary><ol>...</ol></details> -->
|
||||
{% if post.recipe %}
|
||||
<details class="recipe">
|
||||
<summary>recipe</summary>
|
||||
<ol>
|
||||
{% for step in post.recipe.steps.all() %}
|
||||
<li>{{ step.instructions }}</li>
|
||||
{% endfor %}
|
||||
</ol>
|
||||
</details>
|
||||
{% endif %}
|
||||
</div>
|
||||
</article>
|
||||
|
|
@ -27,7 +27,7 @@ class FeedView(FormView):
|
|||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
**super().get_context_data(**kwargs),
|
||||
"posts": Post.objects.order_by("-created_at").all()[:128]
|
||||
"posts": Post.objects.all()[:128]
|
||||
}
|
||||
if self.request.user.is_authenticated:
|
||||
return self.get_authenticated_context_data(context)
|
||||
|
|
|
|||
121
moku/views/recipe.py
Normal file
121
moku/views/recipe.py
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
from django.contrib import messages
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.views.generic import FormView, TemplateView
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
from moku.forms.recipe import RecipeForm, RecipeStepForm
|
||||
from moku.models.recipe import Recipe, RecipeStep
|
||||
|
||||
|
||||
class DeleteRecipeView(LoginRequiredMixin, UserPassesTestMixin, TemplateView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.recipe.delete()
|
||||
messages.success(self.request, _("recipe deleted successfully!"))
|
||||
return redirect("recipe.index")
|
||||
|
||||
@cached_property
|
||||
def recipe(self):
|
||||
return get_object_or_404(
|
||||
Recipe,
|
||||
uuid=self.kwargs.get("uuid"),
|
||||
)
|
||||
|
||||
def test_func(self):
|
||||
return self.request.user.id == self.recipe.created_by.id
|
||||
|
||||
|
||||
class DeleteStepView(LoginRequiredMixin, UserPassesTestMixin, TemplateView):
|
||||
def get(self, request, *args, **kwargs):
|
||||
if self.step.order != (self.step.recipe.steps.count() - 1):
|
||||
messages.error(self.request, _("sorry! you can only delete the last step."))
|
||||
return redirect(self.step.recipe.get_absolute_url())
|
||||
self.step.delete()
|
||||
messages.success(self.request, _("step deleted!"))
|
||||
return redirect(self.step.recipe.get_absolute_url())
|
||||
|
||||
@cached_property
|
||||
def step(self):
|
||||
return get_object_or_404(
|
||||
RecipeStep,
|
||||
recipe__uuid=self.kwargs.get("uuid"),
|
||||
uuid=self.kwargs.get("step"),
|
||||
)
|
||||
|
||||
def test_func(self):
|
||||
return self.request.user.id == self.step.recipe.created_by.id
|
||||
|
||||
|
||||
class EditStepView(LoginRequiredMixin, UserPassesTestMixin, FormView):
|
||||
template_name = "moku/recipe/edit_step.jinja"
|
||||
form_class = RecipeStepForm
|
||||
|
||||
def form_valid(self, form):
|
||||
form.save()
|
||||
messages.success(self.request, _("step updated!"))
|
||||
return redirect(self.step.recipe.get_absolute_url())
|
||||
|
||||
def get_form(self):
|
||||
return self.form_class(instance=self.step, **self.get_form_kwargs())
|
||||
|
||||
@cached_property
|
||||
def step(self):
|
||||
return get_object_or_404(
|
||||
RecipeStep,
|
||||
recipe__uuid=self.kwargs.get("uuid"),
|
||||
uuid=self.kwargs.get("step"),
|
||||
)
|
||||
|
||||
def test_func(self):
|
||||
return self.request.user.id == self.step.recipe.created_by.id
|
||||
|
||||
|
||||
class IndexRecipeView(LoginRequiredMixin, TemplateView):
|
||||
template_name = "moku/recipe/index.jinja"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return {
|
||||
**super().get_context_data(**kwargs),
|
||||
"recipes": Recipe.objects.filter(created_by=self.request.user).order_by("-created_by"),
|
||||
}
|
||||
|
||||
|
||||
class NewRecipeView(LoginRequiredMixin, FormView):
|
||||
template_name = "moku/recipe/form.jinja"
|
||||
form_class = RecipeForm
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.created_by = self.request.user
|
||||
form.save()
|
||||
messages.success(self.request, _("recipe made successfully! now you can start adding the steps."))
|
||||
return redirect(form.instance.get_absolute_url())
|
||||
|
||||
|
||||
class ShowRecipeView(FormView):
|
||||
template_name = "moku/recipe/show.jinja"
|
||||
form_class = RecipeStepForm
|
||||
|
||||
@cached_property
|
||||
def recipe(self):
|
||||
return Recipe.objects.get(uuid=self.kwargs.get("uuid"))
|
||||
|
||||
def form_valid(self, form):
|
||||
if not self.request.user.is_authenticated or self.request.user.id != self.recipe.created_by.id:
|
||||
messages.error(self.request, _("that's not yours!"))
|
||||
return redirect(form.instance.get_absolute_url())
|
||||
order = self.recipe.steps.count()
|
||||
if order >= 16:
|
||||
messages.error(self.request, _("sorry! you can't add any more steps to this recipe."))
|
||||
return redirect(self.recipe.get_absolute_url())
|
||||
form.instance.recipe = self.recipe
|
||||
form.instance.order = order
|
||||
form.save()
|
||||
messages.success(self.request, _("step added successfully!"))
|
||||
return redirect(self.recipe.get_absolute_url())
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
return {
|
||||
**super().get_context_data(**kwargs),
|
||||
"recipe": self.recipe,
|
||||
}
|
||||
Loading…
Reference in a new issue