Skip to content

Commit fb52b93

Browse files
horlasfreezed
authored andcommitted
Add 4 articles about django
* ajout article -upload/update image avatar * ajout article -plusieurs formulaires dans une vue * ajout article -suppresion item form select * ajout article -inclure un template message d erreur * slug enlevé
1 parent f2c540d commit fb52b93

File tree

4 files changed

+617
-0
lines changed

4 files changed

+617
-0
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
Title: Changer une image d'avatar dans un profil utilisateur
2+
Date: 2019-01-18 09:50
3+
Modified: 2019-01-08 08:00
4+
Category: Articles
5+
Tags: Django, Pillow
6+
Slug:
7+
Author: Aurélia Gourbère
8+
9+
10+
# Changer une image d'avatar dans un profil utilisateur
11+
12+
## Contraintes de la fonctionnailité:
13+
14+
* Pouvoir charger une image d'avatar (upload).
15+
* Pouvoir mettre à jour l'avatar (update).
16+
* Redimensionner l'image chargée
17+
* Renommer l'image chargée (date_user_id.jpg)
18+
* Pouvoir envoyer du png comme du jpeg et sortir du jpeg.
19+
* Nettoyer le répertoire de stockage de manière à ce que seule l'image de l'avatar courant soit en stock.
20+
21+
## Librairies utilisées
22+
23+
```django-model-utils``` Fieldtracker [ Doc](https://django-model-utils.readthedocs.io/en/latest/utilities.html)
24+
25+
```Pillow ``` [Doc](https://pillow.readthedocs.io/en/stable/)
26+
27+
## Surcharge de la méthode save() du Model Userprofile
28+
29+
30+
``` models.py ```
31+
32+
Dépendances et bibliothèques
33+
34+
```
35+
from model_utils import FieldTracker
36+
from PIL import Image
37+
from datetime import date
38+
from io import BytesIO
39+
from django.core.files.uploadedfile import InMemoryUploadedFile
40+
import sys
41+
import os
42+
from django.conf import settings
43+
44+
```
45+
Model Userprofile
46+
47+
```
48+
class UserProfile(models.Model):
49+
'''
50+
Custom User Profile
51+
'''
52+
53+
GENDER_CHOICES = (
54+
('H', 'Homme'),
55+
('F', 'Femme'),
56+
)
57+
58+
user = models.OneToOneField(User, on_delete=models.CASCADE)
59+
username = models.CharField(max_length=60, blank=True)
60+
bio = models.TextField(max_length=500, blank=True)
61+
dept = models.CharField(max_length=5, blank=True)
62+
town = models.CharField(max_length=60, blank=True)
63+
birth_year = YearField(null=True, blank=True)
64+
avatar = models.ImageField(null=True, blank=True, upload_to='user_avatar/')
65+
gender = models.CharField('gender' , max_length=1 , choices=GENDER_CHOICES)
66+
created_at = models.DateTimeField(auto_now_add=True)
67+
updated_at = models.DateTimeField(auto_now=True)
68+
69+
# Here we instantiate a Fieltracker to track any fields specially avatar field
70+
tracker = FieldTracker()
71+
72+
```
73+
```
74+
def save(self, *args, **kwargs):
75+
super().save()
76+
77+
# we get here the self avatar condition
78+
# in case of there is no self.avatar as example
79+
# after a clear action.
80+
if self.avatar and self.tracker.has_changed('avatar'):
81+
82+
# keep the upload image path in order to delete it after
83+
upload_image = self.avatar.path
84+
85+
# rename avatar image
86+
avatar_name = '{}-{}.jpg'.format(date.today(), self.user.id)
87+
88+
img = Image.open(upload_image)
89+
90+
# convert all picture to jpg
91+
img = img.convert('RGB')
92+
93+
# resize picture
94+
img = img.resize((140, 140), Image.ANTIALIAS)
95+
96+
# make readable picture
97+
output = BytesIO()
98+
img.save(output, format='JPEG', quality=100)
99+
output.seek(0)
100+
self.avatar = InMemoryUploadedFile(output,
101+
'ImageField',
102+
avatar_name,
103+
'image/jpeg',
104+
sys.getsizeof(output),
105+
None)
106+
107+
super(UserProfile, self).save()
108+
109+
# delete the upload of avatar before resize it
110+
os.remove(upload_image)
111+
112+
# delete old image file
113+
if self.tracker.previous('avatar'):
114+
old_avatar = '{}{}'.format(settings.MEDIA_ROOT, self.tracker.previous('avatar'))
115+
os.remove(old_avatar)
116+
117+
# delete old image file even in case of "clear" image action
118+
if not self.avatar and self.tracker.has_changed('avatar'):
119+
old_avatar = '{}{}'.format(settings.MEDIA_ROOT , self.tracker.previous('avatar'))
120+
os.remove(old_avatar)
121+
122+
```
123+
124+
views.py
125+
126+
```
127+
@login_required
128+
@transaction.atomic
129+
def update_profile(request):
130+
131+
if request.method == 'POST':
132+
133+
profile_form = ProfileForm(request.POST,
134+
request.FILES,
135+
instance=request.user.userprofile)
136+
137+
if profile_form.is_valid():
138+
profile_form.save()
139+
messages.success(request , _('Your profile was successfully updated!'))
140+
return redirect('musicians:profile')
141+
else:
142+
messages.error(request , _('Please correct the error below.'))
143+
#
144+
else:
145+
profile_form = ProfileForm(instance=request.user.userprofile)
146+
147+
return render(request , 'musicians/update_profile.html', {
148+
149+
'profile_form': profile_form
150+
})
151+
```
152+
153+
Template : l'aspect frontend n'est absolument pas mis en valeur, mais le code fonctionne.
154+
155+
``` update_profile.html ```
156+
157+
```
158+
{% extends 'core/base.html' %}
159+
{% load static %}
160+
{% block content %}
161+
162+
<div class="container">
163+
<div class="row">
164+
<div class="col s12 ">
165+
166+
<form method="post" enctype="multipart/form-data">
167+
{% csrf_token %}
168+
{{ profile_form.as_p }}
169+
<input type="submit" value="Save">
170+
171+
</form>
172+
173+
</div>
174+
</div>
175+
</div>
176+
177+
178+
{% endblock %}
179+
```
180+
181+
``` profile.html ```
182+
183+
```
184+
{% extends 'core/base.html' %}
185+
{% load static %}
186+
187+
{% block content %}
188+
<div class="container">
189+
<div class="row">
190+
<div class="col s12 ">
191+
192+
<h2>Profil de {{user.userprofile.username}}</h2>
193+
<h2>email :{{user.email}}</h2>
194+
<h4>Bio : {{user.userprofile.bio}}</h4>
195+
{% if user.userprofile.avatar %}
196+
<img src="{{ user.userprofile.avatar.url }}" />
197+
{% else %}
198+
<img src="{% static 'core/img/0.jpg' %}" />
199+
{% endif %}
200+
201+
202+
<a href="{% url 'musicians:update_profile' %}" class="waves-effect waves-light btn">button</a>
203+
</div>
204+
</div>
205+
</div>
206+
207+
{% endblock %}
208+
209+
```
210+
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
Title: Réutiliser le composant messages d'erreurs .
2+
Date: 2019-01-18 15:25
3+
Modified: 2019-01-17 08:00
4+
Category: Articles
5+
Tags: Django, Forms, CBV
6+
Slug:
7+
Author: Aurélia Gourbère
8+
9+
10+
# Réutiliser un template d'affichage des messages d'erreurs de formulaire
11+
12+
## Contexte
13+
14+
Pour éviter la redondance de l'affichage dans un gabarit des messages d'erreur d'un formulaire . Nous pouvons faire un template réutilisable.
15+
16+
## Template form_errors.html
17+
18+
Dans le cas où notre projet a plusieurs apps, nous faisons le choix de le mettre dans le répertoire core/template/form_erros.html
19+
20+
```
21+
{% block content %}
22+
23+
{% if form.errors %}
24+
{% for field in form.%}
25+
{% for error in field.errors %}
26+
<div class="alert alert-danger">
27+
<strong>{{ error|escape }}</strong>
28+
</div>
29+
{% endfor %}
30+
{% endfor %}
31+
{% for error in form.non_field_errors %}
32+
<div class="alert alert-danger">
33+
<strong>{{ error|escape }}</strong>
34+
</div>
35+
{% endfor %}
36+
{% endif %}
37+
38+
39+
{% endblock %}
40+
```
41+
42+
43+
## Réutilisation dans le reste du projet
44+
45+
Pour chaque formulaire du projet nous pouvons à présent appeler ce petit bout de code pour l'affichage des erreurs possibles générées par n'importe lequel de nos formulaires:
46+
47+
```
48+
<!--profile form-->
49+
<div class="col s7">
50+
<form action='{% url "musicians:update_data" %}' method="post" >
51+
{% csrf_token %}
52+
53+
{% include 'core/form_errors.html' with form=profil_form %}
54+
55+
{{ profile_form.as_p }}
56+
<button class="btn-floating btn-large waves-effect waves-light " type="submit" >
57+
<i class="material-icons">check</i></button>
58+
59+
</form>
60+
</div>
61+
</div>
62+
```
63+

0 commit comments

Comments
 (0)