sandwitches 2.2.0__tar.gz → 2.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. {sandwitches-2.2.0 → sandwitches-2.3.0}/PKG-INFO +1 -1
  2. {sandwitches-2.2.0 → sandwitches-2.3.0}/pyproject.toml +1 -1
  3. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/admin.py +23 -2
  4. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/forms.py +13 -0
  5. sandwitches-2.3.0/src/sandwitches/migrations/0007_historicalrecipe_price_recipe_price_order.py +86 -0
  6. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/models.py +35 -0
  7. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/navbar.html +10 -5
  8. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/recipe_header.html +8 -0
  9. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/partials/recipe_list.html +7 -0
  10. sandwitches-2.3.0/src/sandwitches/templates/profile.html +95 -0
  11. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/urls.py +1 -0
  12. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/views.py +16 -0
  13. {sandwitches-2.2.0 → sandwitches-2.3.0}/README.md +0 -0
  14. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/__init__.py +0 -0
  15. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/api.py +0 -0
  16. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/asgi.py +0 -0
  17. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/feeds.py +0 -0
  18. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/locale/nl/LC_MESSAGES/django.mo +0 -0
  19. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/locale/nl/LC_MESSAGES/django.po +0 -0
  20. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/migrations/0001_initial.py +0 -0
  21. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/migrations/0002_historicalrecipe_servings_recipe_servings.py +0 -0
  22. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/migrations/0003_setting.py +0 -0
  23. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/migrations/0004_alter_setting_ai_api_key_and_more.py +0 -0
  24. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/migrations/0005_rating_comment.py +0 -0
  25. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/migrations/0006_historicalrecipe_is_highlighted_and_more.py +0 -0
  26. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/migrations/__init__.py +0 -0
  27. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/settings.py +0 -0
  28. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/storage.py +0 -0
  29. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/tasks.py +0 -0
  30. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/admin/admin_base.html +0 -0
  31. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/admin/confirm_delete.html +0 -0
  32. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/admin/dashboard.html +0 -0
  33. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/admin/rating_list.html +0 -0
  34. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/admin/recipe_form.html +0 -0
  35. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/admin/recipe_list.html +0 -0
  36. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/admin/tag_form.html +0 -0
  37. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/admin/tag_list.html +0 -0
  38. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/admin/task_detail.html +0 -0
  39. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/admin/task_list.html +0 -0
  40. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/admin/user_form.html +0 -0
  41. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/admin/user_list.html +0 -0
  42. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/base.html +0 -0
  43. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/base_beer.html +0 -0
  44. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/carousel_scripts.html +0 -0
  45. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/favorites_search_form.html +0 -0
  46. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/footer.html +0 -0
  47. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/ingredients_scripts.html +0 -0
  48. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/ingredients_section.html +0 -0
  49. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/instructions_section.html +0 -0
  50. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/language_dialog.html +0 -0
  51. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/rating_section.html +0 -0
  52. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/search_form.html +0 -0
  53. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/search_scripts.html +0 -0
  54. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/side_menu.html +0 -0
  55. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/components/user_menu.html +0 -0
  56. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/detail.html +0 -0
  57. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/favorites.html +0 -0
  58. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/index.html +0 -0
  59. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/login.html +0 -0
  60. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/recipe_form.html +0 -0
  61. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/setup.html +0 -0
  62. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templates/signup.html +0 -0
  63. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templatetags/__init__.py +0 -0
  64. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templatetags/custom_filters.py +0 -0
  65. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/templatetags/markdown_extras.py +0 -0
  66. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/utils.py +0 -0
  67. {sandwitches-2.2.0 → sandwitches-2.3.0}/src/sandwitches/wsgi.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: sandwitches
3
- Version: 2.2.0
3
+ Version: 2.3.0
4
4
  Summary: Add your description here
5
5
  Author: Martyn van Dijke
6
6
  Author-email: Martyn van Dijke <martijnvdijke600@gmail.com>
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "sandwitches"
3
- version = "2.2.0"
3
+ version = "2.3.0"
4
4
  description = "Add your description here"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,7 +1,7 @@
1
1
  from django.contrib import admin
2
2
  from django.contrib.auth import get_user_model
3
3
  from django.contrib.auth.admin import UserAdmin
4
- from .models import Recipe, Tag, Rating, Setting
4
+ from .models import Recipe, Tag, Rating, Setting, Order
5
5
  from django.utils.html import format_html
6
6
  from import_export import resources
7
7
  from import_export.admin import ImportExportModelAdmin
@@ -24,6 +24,11 @@ class RatingResource(resources.ModelResource):
24
24
  model = Rating
25
25
 
26
26
 
27
+ class OrderResource(resources.ModelResource):
28
+ class Meta:
29
+ model = Order
30
+
31
+
27
32
  User = get_user_model()
28
33
 
29
34
 
@@ -42,7 +47,14 @@ class CustomUserAdmin(UserAdmin):
42
47
  @admin.register(Recipe)
43
48
  class RecipeAdmin(ImportExportModelAdmin):
44
49
  resource_classes = [RecipeResource]
45
- list_display = ("title", "uploaded_by", "created_at", "is_highlighted", "show_url")
50
+ list_display = (
51
+ "title",
52
+ "uploaded_by",
53
+ "price",
54
+ "created_at",
55
+ "is_highlighted",
56
+ "show_url",
57
+ )
46
58
  list_editable = ("is_highlighted",)
47
59
  readonly_fields = ("created_at", "updated_at")
48
60
 
@@ -67,3 +79,12 @@ class TagAdmin(ImportExportModelAdmin):
67
79
  @admin.register(Rating)
68
80
  class RatingAdmin(ImportExportModelAdmin):
69
81
  resource_classes = [RatingResource]
82
+
83
+
84
+ @admin.register(Order)
85
+ class OrderAdmin(ImportExportModelAdmin):
86
+ resource_classes = [OrderResource]
87
+ list_display = ("id", "user", "recipe", "status", "total_price", "created_at")
88
+ list_filter = ("status", "created_at")
89
+ search_fields = ("user__username", "recipe__title")
90
+ readonly_fields = ("total_price", "created_at", "updated_at")
@@ -85,6 +85,18 @@ class UserSignupForm(UserCreationForm, BaseUserFormMixin):
85
85
  return user
86
86
 
87
87
 
88
+ class UserProfileForm(forms.ModelForm):
89
+ class Meta:
90
+ model = User
91
+ fields = (
92
+ "first_name",
93
+ "last_name",
94
+ "email",
95
+ "avatar",
96
+ "bio",
97
+ )
98
+
99
+
88
100
  class UserEditForm(forms.ModelForm):
89
101
  class Meta:
90
102
  model = User
@@ -124,6 +136,7 @@ class RecipeForm(forms.ModelForm):
124
136
  "description",
125
137
  "ingredients",
126
138
  "instructions",
139
+ "price",
127
140
  ]
128
141
  widgets = {
129
142
  "image": forms.FileInput(),
@@ -0,0 +1,86 @@
1
+ # Generated by Django 6.0.1 on 2026-01-20 10:49
2
+
3
+ import django.db.models.deletion
4
+ from django.conf import settings
5
+ from django.db import migrations, models
6
+
7
+
8
+ class Migration(migrations.Migration):
9
+ dependencies = [
10
+ ("sandwitches", "0006_historicalrecipe_is_highlighted_and_more"),
11
+ ]
12
+
13
+ operations = [
14
+ migrations.AddField(
15
+ model_name="historicalrecipe",
16
+ name="price",
17
+ field=models.DecimalField(
18
+ blank=True,
19
+ decimal_places=2,
20
+ max_digits=6,
21
+ null=True,
22
+ verbose_name="Price (€)",
23
+ ),
24
+ ),
25
+ migrations.AddField(
26
+ model_name="recipe",
27
+ name="price",
28
+ field=models.DecimalField(
29
+ blank=True,
30
+ decimal_places=2,
31
+ max_digits=6,
32
+ null=True,
33
+ verbose_name="Price (€)",
34
+ ),
35
+ ),
36
+ migrations.CreateModel(
37
+ name="Order",
38
+ fields=[
39
+ (
40
+ "id",
41
+ models.BigAutoField(
42
+ auto_created=True,
43
+ primary_key=True,
44
+ serialize=False,
45
+ verbose_name="ID",
46
+ ),
47
+ ),
48
+ (
49
+ "status",
50
+ models.CharField(
51
+ choices=[
52
+ ("PENDING", "Pending"),
53
+ ("COMPLETED", "Completed"),
54
+ ("CANCELLED", "Cancelled"),
55
+ ],
56
+ default="PENDING",
57
+ max_length=20,
58
+ ),
59
+ ),
60
+ ("total_price", models.DecimalField(decimal_places=2, max_digits=6)),
61
+ ("created_at", models.DateTimeField(auto_now_add=True)),
62
+ ("updated_at", models.DateTimeField(auto_now=True)),
63
+ (
64
+ "recipe",
65
+ models.ForeignKey(
66
+ on_delete=django.db.models.deletion.CASCADE,
67
+ related_name="orders",
68
+ to="sandwitches.recipe",
69
+ ),
70
+ ),
71
+ (
72
+ "user",
73
+ models.ForeignKey(
74
+ on_delete=django.db.models.deletion.CASCADE,
75
+ related_name="orders",
76
+ to=settings.AUTH_USER_MODEL,
77
+ ),
78
+ ),
79
+ ],
80
+ options={
81
+ "verbose_name": "Order",
82
+ "verbose_name_plural": "Orders",
83
+ "ordering": ("-created_at",),
84
+ },
85
+ ),
86
+ ]
@@ -89,6 +89,9 @@ class Recipe(models.Model):
89
89
  ingredients = models.TextField(blank=True)
90
90
  instructions = models.TextField(blank=True)
91
91
  servings = models.IntegerField(default=1, validators=[MinValueValidator(1)])
92
+ price = models.DecimalField(
93
+ max_digits=6, decimal_places=2, null=True, blank=True, verbose_name="Price (€)"
94
+ )
92
95
  uploaded_by = models.ForeignKey(
93
96
  settings.AUTH_USER_MODEL,
94
97
  related_name="recipes",
@@ -216,3 +219,35 @@ class Rating(models.Model):
216
219
 
217
220
  def __str__(self):
218
221
  return f"{self.recipe} — {self.score} by {self.user}"
222
+
223
+
224
+ class Order(models.Model):
225
+ STATUS_CHOICES = (
226
+ ("PENDING", "Pending"),
227
+ ("COMPLETED", "Completed"),
228
+ ("CANCELLED", "Cancelled"),
229
+ )
230
+
231
+ user = models.ForeignKey(
232
+ settings.AUTH_USER_MODEL, related_name="orders", on_delete=models.CASCADE
233
+ )
234
+ recipe = models.ForeignKey(Recipe, related_name="orders", on_delete=models.CASCADE)
235
+ status = models.CharField(max_length=20, choices=STATUS_CHOICES, default="PENDING")
236
+ total_price = models.DecimalField(max_digits=6, decimal_places=2)
237
+ created_at = models.DateTimeField(auto_now_add=True)
238
+ updated_at = models.DateTimeField(auto_now=True)
239
+
240
+ class Meta:
241
+ ordering = ("-created_at",)
242
+ verbose_name = "Order"
243
+ verbose_name_plural = "Orders"
244
+
245
+ def save(self, *args, **kwargs):
246
+ if not self.recipe.price: # ty:ignore[possibly-missing-attribute]
247
+ raise ValueError("Cannot order a recipe without a price.")
248
+ if not self.total_price:
249
+ self.total_price = self.recipe.price # ty:ignore[possibly-missing-attribute]
250
+ super().save(*args, **kwargs)
251
+
252
+ def __str__(self):
253
+ return f"Order #{self.pk} - {self.user} - {self.recipe}"
@@ -15,11 +15,16 @@
15
15
  </button>
16
16
 
17
17
  {% if user.is_authenticated %}
18
- {% if user.avatar %}
19
- <img src="{{ user.avatar.url }}" class="circle" data-ui="#user-menu">
20
- {% else %}
21
- <img src="https://www.w3schools.com/howto/img_avatar.png" class="circle" data-ui="#user-menu">
22
- {% endif %}
18
+ <a href="{% url 'user_profile' %}">
19
+ {% if user.avatar %}
20
+ <img src="{{ user.avatar.url }}" class="circle">
21
+ {% else %}
22
+ <img src="https://www.w3schools.com/howto/img_avatar.png" class="circle">
23
+ {% endif %}
24
+ </a>
25
+ <button class="circle transparent" data-ui="#user-menu">
26
+ <i>more_vert</i>
27
+ </button>
23
28
  {% else %}
24
29
  <a href="{% url 'login' %}"><button class="chip transparent border white-text">{% trans "Login" %}</button></a>
25
30
  <a href="{% url 'signup' %}"><button class="chip primary">{% trans "Sign up" %}</button></a>
@@ -30,3 +30,11 @@
30
30
  </div>
31
31
  {% endif %}
32
32
  </div>
33
+
34
+ <div class="space"></div>
35
+ {% if recipe.price %}
36
+ <div class="row align-center">
37
+ <i class="primary-text">euro_symbol</i>
38
+ <h5 class="bold ml-1">{{ recipe.price }}</h5>
39
+ </div>
40
+ {% endif %}
@@ -26,6 +26,13 @@
26
26
  <div class="padding">
27
27
  <h5 class="truncate pointer" onclick="location.href='{% url 'recipe_detail' recipe.slug %}';">{{ recipe.title }}</h5>
28
28
 
29
+ {% if recipe.price %}
30
+ <div class="row align-center mb-1">
31
+ <i class="small-text primary-text">euro_symbol</i>
32
+ <span class="bold">{{ recipe.price }}</span>
33
+ </div>
34
+ {% endif %}
35
+
29
36
  <div class="row align-center">
30
37
  {% if recipe.uploaded_by %}
31
38
  <a href="{% url 'index' %}?uploader={{ recipe.uploaded_by.username|urlencode }}" class="row align-center" style="color: inherit; text-decoration: none;">
@@ -0,0 +1,95 @@
1
+ {% extends "base.html" %}
2
+ {% load static i18n %}
3
+ {% block title %}{% trans "Your Profile" %}{% endblock %}
4
+
5
+ {% block content %}
6
+ <div class="large-space"></div>
7
+
8
+ <div class="grid">
9
+ <div class="s12 m10 l8 xl6 middle-align center-align" style="margin: 0 auto;">
10
+ <article class="round elevate">
11
+ <div class="padding">
12
+ <h4 class="center-align primary-text">{% trans "Edit your profile" %}</h4>
13
+ </div>
14
+
15
+ <form method="post" enctype="multipart/form-data" novalidate>
16
+ {% csrf_token %}
17
+
18
+ {% if form.non_field_errors %}
19
+ <div class="padding error surface round mb">
20
+ {% for err in form.non_field_errors %}
21
+ <div class="row align-center">
22
+ <i class="error-text">warning</i>
23
+ <span class="error-text">{{ err }}</span>
24
+ </div>
25
+ {% endfor %}
26
+ </div>
27
+ {% endif %}
28
+
29
+ <div class="grid">
30
+ <div class="s12 m6">
31
+ <div class="field label border round {% if form.first_name.errors %}error{% endif %}">
32
+ <input type="text" name="{{ form.first_name.name }}" id="{{ form.first_name.id_for_label }}" value="{{ form.first_name.value|default:'' }}">
33
+ <label>{% trans "First name" %}</label>
34
+ {% if form.first_name.errors %}
35
+ <span class="helper error-text">{{ form.first_name.errors.0 }}</span>
36
+ {% endif %}
37
+ </div>
38
+ </div>
39
+ <div class="s12 m6">
40
+ <div class="field label border round {% if form.last_name.errors %}error{% endif %}">
41
+ <input type="text" name="{{ form.last_name.name }}" id="{{ form.last_name.id_for_label }}" value="{{ form.last_name.value|default:'' }}">
42
+ <label>{% trans "Last name" %}</label>
43
+ {% if form.last_name.errors %}
44
+ <span class="helper error-text">{{ form.last_name.errors.0 }}</span>
45
+ {% endif %}
46
+ </div>
47
+ </div>
48
+ </div>
49
+
50
+ <div class="field label border round {% if form.email.errors %}error{% endif %}">
51
+ <input type="email" name="{{ form.email.name }}" id="{{ form.email.id_for_label }}" value="{{ form.email.value|default:'' }}">
52
+ <label>{% trans "Email" %}</label>
53
+ {% if form.email.errors %}
54
+ <span class="helper error-text">{{ form.email.errors.0 }}</span>
55
+ {% endif %}
56
+ </div>
57
+
58
+ <div class="field label border round textarea {% if form.bio.errors %}error{% endif %}">
59
+ <textarea name="{{ form.bio.name }}" id="{{ form.bio.id_for_label }}" rows="3">{{ form.bio.value|default:'' }}</textarea>
60
+ <label>{% trans "Bio" %}</label>
61
+ {% if form.bio.errors %}
62
+ <span class="helper error-text">{{ form.bio.errors.0 }}</span>
63
+ {% endif %}
64
+ </div>
65
+
66
+ <div class="field middle-align {% if form.avatar.errors %}error{% endif %}">
67
+ <nav>
68
+ <div class="max">
69
+ <label class="button border transparent round">
70
+ <input type="file" name="{{ form.avatar.name }}" id="{{ form.avatar.id_for_label }}" accept="image/*">
71
+ <i>upload</i>
72
+ <span>{% trans "Upload Profile Picture" %}</span>
73
+ </label>
74
+ </div>
75
+ {% if user.avatar %}
76
+ <img src="{{ user.avatar.url }}" class="circle small">
77
+ {% endif %}
78
+ </nav>
79
+ {% if form.avatar.errors %}
80
+ <span class="helper error-text">{{ form.avatar.errors.0 }}</span>
81
+ {% endif %}
82
+ </div>
83
+
84
+ <div class="large-space"></div>
85
+
86
+ <nav class="right-align">
87
+ <a class="button transparent border round" href="{% url 'index' %}">{% trans "Cancel" %}</a>
88
+ <button type="submit" class="button primary round">{% trans "Save changes" %}</button>
89
+ </nav>
90
+
91
+ </form>
92
+ </article>
93
+ </div>
94
+ </div>
95
+ {% endblock %}
@@ -33,6 +33,7 @@ urlpatterns = [
33
33
  path("signup/", views.signup, name="signup"),
34
34
  path("login/", views.CustomLoginView.as_view(), name="login"),
35
35
  path("logout/", LogoutView.as_view(next_page="index"), name="logout"),
36
+ path("profile/", views.user_profile, name="user_profile"),
36
37
  path("admin/", admin.site.urls),
37
38
  path("api/", api.urls),
38
39
  path("media/<path:file_path>", views.media, name="media"),
@@ -14,6 +14,7 @@ from .forms import (
14
14
  RatingForm,
15
15
  UserEditForm,
16
16
  TagForm,
17
+ UserProfileForm,
17
18
  )
18
19
  from django.http import HttpResponseBadRequest
19
20
  from django.conf import settings
@@ -645,3 +646,18 @@ def media(request, file_path=None):
645
646
 
646
647
  response = FileResponse(open(full_path, "rb"), as_attachment=True)
647
648
  return response
649
+
650
+
651
+ @login_required
652
+ def user_profile(request):
653
+ if request.method == "POST":
654
+ form = UserProfileForm(request.POST, request.FILES, instance=request.user)
655
+ if form.is_valid():
656
+ form.save()
657
+ messages.success(request, _("Profile updated successfully."))
658
+ return redirect("user_profile")
659
+ else:
660
+ form = UserProfileForm(instance=request.user)
661
+ return render(
662
+ request, "profile.html", {"form": form, "version": sandwitches_version}
663
+ )
File without changes