create review from scratch and answer works

This commit is contained in:
yann 2025-05-02 09:35:02 +02:00
parent 83c75e95f3
commit aceb42c2e6
34 changed files with 464 additions and 95 deletions

View File

@ -25,10 +25,10 @@ import authentication.views, reviews.views
urlpatterns = [
path('admin/', admin.site.urls),
path('home/', reviews.views.home, name='home'),
# path('', LoginView.as_view(
path('', authentication.views.login_page, name='login'),
# template_name='authentication/login.html',
# redirect_authenticated_user=True), name='login'),
path('', LoginView.as_view(
# path('', authentication.views.login_page, name='login'),
template_name='authentication/login.html',
redirect_authenticated_user=True), name='login'),
path('pwd-change/', PasswordChangeView.as_view(
template_name='authentication/pwd_change.html'), name='pwd-change'),
path('pwd-change-done/', PasswordChangeDoneView.as_view(
@ -43,6 +43,7 @@ urlpatterns = [
path('ticket/<int:ticket_id>/', reviews.views.ticket, name='ticket-detail'),
path('ticket/<int:ticket_id>/update/', reviews.views.update_ticket, name='ticket-update'),
path('ticket/<int:ticket_id>/delete/', reviews.views.delete_ticket, name='ticket-delete'),
path('ticket/<int:ticket_id>/review/', reviews.views.ticket_review, name='review-ticket'),
path('review/<int:review_id>/', reviews.views.review, name='review-detail'),
path('review/<int:review_id>/update/', reviews.views.update_review, name='review-update'),
path('review/<int:review_id>/delete/', reviews.views.delete_review, name='review-delete'),

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 305 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -5,13 +5,14 @@ from . import models
class TicketForm(forms.ModelForm):
class Meta:
model = models.Ticket
fields = ['title', 'body', 'image']
fields = ['title', 'desc', 'image']
class ReviewForm(forms.ModelForm):
CHOICES = [0, 1, 2, 3, 4, 5]
CHOICES = {'0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5}
rating = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES)
class Meta:
model = models.Review
fields = ['headline', 'body']
fields = ['headline', 'rating', 'comment']

View File

@ -0,0 +1,18 @@
# Generated by Django 5.2 on 2025-04-26 12:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reviews', '0002_userfollows_followed_user_userfollows_user_and_more'),
]
operations = [
migrations.AlterField(
model_name='ticket',
name='image',
field=models.ImageField(upload_to=''),
),
]

View File

@ -0,0 +1,27 @@
# Generated by Django 5.2 on 2025-05-01 12:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reviews', '0003_alter_ticket_image'),
]
operations = [
migrations.RenameField(
model_name='ticket',
old_name='body',
new_name='desc',
),
migrations.RemoveField(
model_name='review',
name='body',
),
migrations.AddField(
model_name='review',
name='comment',
field=models.CharField(blank=True, max_length=8192, verbose_name='commentaire'),
),
]

View File

@ -0,0 +1,20 @@
# Generated by Django 5.2 on 2025-05-01 17:23
import django.utils.timezone
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('reviews', '0004_rename_body_ticket_desc_remove_review_body_and_more'),
]
operations = [
migrations.AddField(
model_name='userfollows',
name='starting_date',
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
preserve_default=False,
),
]

View File

@ -1,18 +1,30 @@
from django.core.validators import MinValueValidator, MaxValueValidator
from django.conf import settings
from django.db import models
from PIL import Image
class Ticket(models.Model):
# Your Ticket model definition goes here
title = models.CharField("Titre", max_length=100)
topic = models.CharField(max_length=100)
body = models.CharField("Description", max_length=8192)
desc = models.CharField("Description", max_length=8192)
user = models.ForeignKey(
to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
image = models.ImageField(upload_to="uploads/")
image = models.ImageField()
time_created = models.DateTimeField(auto_now_add=True)
review = models.ForeignKey(to=Review, on_delete=models.SET_NULL)
IMAGE_SIZE = (400, 400)
def resize_image(self):
image = Image.open(self.image)
image.thumbnail(self.IMAGE_SIZE)
image.save(self.image.path)
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
self.resize_image()
class Review(models.Model):
@ -21,7 +33,7 @@ class Review(models.Model):
# validates that rating must be between 0 and 5
validators=[MinValueValidator(0), MaxValueValidator(5)])
headline = models.CharField("titre", max_length=128)
body = models.CharField("description", max_length=8192, blank=True)
comment = models.CharField("commentaire", max_length=8192, blank=True)
user = models.ForeignKey(
to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
time_created = models.DateTimeField(auto_now_add=True)
@ -31,6 +43,7 @@ class UserFollows(models.Model):
# Your UserFollows model definition goes here
user = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="following", null=True)
followed_user = models.ForeignKey(to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="followed", null=True)
starting_date = models.DateTimeField(auto_now_add=True)
class Meta:
# ensures we don't get multiple UserFollows instances

View File

@ -1,12 +1,23 @@
{% extends 'base.html' %}
{% block nav %}
<nav>
<a href="{% url 'flux' %}">Flux</a>
<a href="{% url 'posts' %}">Posts</a>
<a href="{% url 'subscribed' %}">Abonnements</a>
<a href="{% url 'logout' %}">Se déconnecter</a>
</nav>
{% endblock %}
{% block content %}
<h2> Flux </h2>
{% endblock %}
<div class="container">
<div class="row">
<div class="d-flex justify-content-center mt-5">
<h2> FLux </h2>
</div>
</div>
<div class="row">
{% for ticket in tickets %}
{% include 'reviews/ticket_detail.html' %}
<div class="col-3 align-self-end">
<a href="{% url 'review-ticket' ticket.id %}" type="button" class="btn btn-primary">Créer une critique</a>
</div>
</div>
{% endfor %}
</div>
</div>
{% endblock %}

View File

@ -16,8 +16,16 @@
</div>
</div>
<div class="row">
{% for ticket in tickets %}
{% include 'reviews/ticket_detail.html' %}
{% for ticket in tickets %}
{% include 'reviews/ticket_detail.html' %}
<div class="col-6 d-flex justify-content-end mb-2">
{% if ticket.review is not True %}
<div class="col-4 align-self-end">
<a href="{% url 'review-ticket' ticket.id %}" type="button" class="btn btn-primary">Créer une critique</a>
</div>
{% endif %}
</div>
</div>
{% endfor %}
</div>

View File

@ -1,12 +1,46 @@
{% extends 'base.html' %}
{% block nav %}
<nav>
<a href="{% url 'flux' %}">Flux</a>
<a href="{% url 'posts' %}">Posts</a>
<a href="{% url 'subscribed' %}">Abonnements</a>
<a href="{% url 'logout' %}">Se déconnecter</a>
</nav>
{% endblock %}
{% block content %}
<h2> Posts </h2>
<div class="container">
<div class="row">
<div class="d-flex justify-content-center mt-5">
<h2> Posts </h2>
</div>
</div>
<div class="row d-flex justify-content-center mt-4 mb-3">
{% for ticket in tickets %}
{% include 'reviews/ticket_detail.html' %}
<div class="col-4 d-flex justify-content-end mb-2">
{% if perms.reviews.change_ticket %}
<div class="col-3 align-self-end">
<a href="{% url 'ticket-update' ticket.id %}" type="button" class="btn btn-primary">Modifier</a>
</div>
<div class="col-3 align-self-end">
<a href="{% url 'ticket-delete' ticket.id %}" type="button" class="btn btn-danger">Supprimer</a>
</div>
</div>
{% endif %}
</div>
{% endfor %}
{% for review in reviews %}
{% include 'reviews/review_detail.html' %}
<div class="col d-flex justify-content-end mb-2">
{% if perms.reviews.change_ticket %}
<div class="d-flex align-self-end m-2">
<a href="{% url 'review-update' review.id %}" type="button" class="btn btn-primary">Modifier</a>
</div>
<div class="d-flex align-self-end m-2">
<a href="{% url 'review-delete' review.id %}" type="button" class="btn btn-danger">Supprimer</a>
</div>
</div>
{% endif %}
</div>
{% endfor %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,7 @@
{% extends 'base.html' %}
{% block content %}
{% include 'reviews/review_detail.html' %}
{% endblock %}

View File

@ -3,20 +3,28 @@
{% block content %}
<div class="container">
<div class="row">
<div class="row mt-3">
<div class="col d-flex justify-content-center">
<h2>Créer une critique</h2>
</div>
</div>
<div class="row border border-3 border-secondary-subtle">
<div class="row border border-3 border-secondary-subtle mb-3 justify-content-center">
<h3> Livre/Article </h3>
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<label for="title">Titre</label>
<div class="d-flex flex-grow-1 mb-4"> </div>
<div class="col-4 mb-2">
<form method="post" enctype="multipart/form-data">
{% include 'reviews/ticket_form.html' %}
{% csrf_token %}
</div>
</div>
<div class="row border border-3 border-secondary-subtle">
<div class="row border border-3 border-secondary-subtle justify-content-center">
<h3>Critique</h3>
<div class="col-4 mb-2">
{% include 'reviews/review_form.html' %}
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary"> Envoyer </button>
</div>
</form>
</div>
</div>
</div>

View File

@ -0,0 +1,16 @@
{% extends 'base.html' %}
{% block content %}
<div class="container text-center" style="height: 100vh">
<div class="row h-100 align-items-center">
<form method="post">
{% csrf_token %}
<p> Êtes vous sûr de vouloir supprimer cette critique ? </p>
<button type="submit" class="btn btn-warning">Supprimer</button>
</form>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,18 @@
<div class="row border border-2 border-secondary-subtle my-3">
<div class="col-8 mb-4">
{% if request.user == review.user %}
vous avez publié une critique
{% else %}
{{ review.user }} a publié une critique
{% endif %}
</div>
<div class="col-4 d-flex justify-content-end">
{{ review.time_created }}
</div>
<span style="font-size: 22px">{{ review.headline }} - {{ review.rating }} ★ </span>
<p> {{ review.comment }} </p>
<div class="d-flex justify-content-center border border-3 mb-3">
{% include 'reviews/ticket_detail.html' with ticket=review.ticket %}
</div>
</div>

View File

@ -1,13 +1,11 @@
<form method='post' enctype='multipart/form-data'>
{% csrf_token %}
<label for='headline'> Titre: </label>
<div class="d-flex justify-content-center mb-4">{{ review_form.headline }}</div>
<label for='rating'>Note: </label>
<div class="d-flex justify-content-center mb-4">{{ review_form.rating }}</div>
<label for='body'>Description: </label>
<div class="d-flex justify-content-center mb-4">{{ review_form.body }}</div>
<div class="d-flex justify-content-end">
<button type='submit' class='btn btn-primary ml-auto'> Envoyer</button>
</div>
</form>
<label for='headline'>Titre:</label></br>
<div class="d-flex justify-content-center mb-4">{{ review_form.headline }}</div>
<label for='rating'>Note: </label>
<div class=" d-flex justify-content-around mb-4">
{% for choice in review_form.rating %}
<span>{{ choice }}</span>
{% endfor %}
</div>
<label for="comment">Commentaire: </label>
<div class="d-flex justify-content-center mb-4">{{ review_form.comment }}</div>

View File

@ -0,0 +1,10 @@
<form method='post' enctype='multipart/form-data'>
{% csrf_token %}
{{ review_form.headline }}
{% for radio in review_form.rating %}
<div class="radio">
{{ radio }}
</div>
{% endfor %}
</form>

View File

@ -0,0 +1,33 @@
{% extends 'base.html' %}
{% block content %}
<div class="container">
<div class="row mt-3">
<div class="col d-flex justify-content-center">
<h2>Créer une critique</h2>
</div>
</div>
<div class="row border border-3 border-secondary-subtle">
<p>Vous êtes en train de répondre à </p>
<div class="col d-flex justify-content-center mb-2">
{% include 'reviews/ticket_detail.html' %}</div>
</div>
</div>
<div class="row border border-3 border-secondary-subtle">
<h3>Critique</h3>
<div class="d-flex justify-content-center mb-2">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% include 'reviews/review_form.html' %}
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary"> Envoyer </button>
</div>
<form>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,42 @@
{% extends 'base.html' %}
{% block content %}
<div class="container">
<div class="row mt-4">
<div class="d-flex justify-content-center">
<h2>Modifier votre critique</h2>
</div>
</div>
<div class="row border border-3">
<div class="col-8">
<p>Vous êtes en train de poster en réponse à</p>
</div>
<div class="d-flex justify-content-center">
{% include 'reviews/ticket_detail.html' with ticket=review.ticket %}
</div>
</div>
</div>
<div class="row border border-3 p-2">
<div class="col-3">
<p>Critique</p>
</div>
<div class="d-flex justify-content-center">
<div class="col-4 mb-2">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{% include 'reviews/review_form.html' %}
<div class="d-flex justify-content-end">
<button type="submit" class="btn btn-primary"> Envoyer </button>
</div>
</form>
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -7,9 +7,17 @@
<div class="col-md-auto form-group mt-5">
<h2> Créer un ticket </h2>
</div>
<div class="d-flex justify-content-center border border-3 border-secondary-subtle mt-5">
<div class="col-4 mt-5">
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ ticket_form.as_p }}
<div class="d-flex justify-content-end mb-4">
<button type="submit" class="btn btn-primary"> Envoyer </button>
</div>
</form>
</div>
</div>
<div class="d-flex justify-content-center mt-5">
{% include 'reviews/ticket_form.html' %}
</div>
</div>

View File

@ -1,28 +1,17 @@
<div class="row border border-2 border-secondary-subtle my-3">
<div class="col-8">
<div class="col-8 mb-4">
{% if request.user == ticket.user %}
vous avez demandé une critique
<strong>vous</strong> avez demandé une critique
{% else %}
{{ ticket.user }} a demandé une critique
<strong>{{ ticket.user }}</strong> a demandé une critique
{% endif %}
</div>
<div class="col-4 d-flex justify-content-end">
{{ ticket.time_created }}
</div>
<p> {{ ticket.title }} </p>
<p> {{ ticket.body }} </p>
<p><strong> {{ ticket.title }} </strong></p>
<p> {{ ticket.desc }} </p>
<div class="col-3 mb-2">
<img src="{{ ticket.image.url }}" class="img" style="width: 200px; height: auto" alt="Couverture de {{ ticket.title }}">
</div>
<div class="col-4 d-flex justify-content-end mb-2">
{% if request.user == ticket.user %}
<div class="col-3 align-self-end">
<a href="{% url 'ticket-update' ticket.id %}" type="button" class="btn btn-primary">Modifier</a>
</div>
<div class="col-3 align-self-end">
<a href="{% url 'ticket-delete' ticket.id %}" type="button" class="btn btn-danger">Supprimer</a>
</div>
{% endif %}
</div>
</div>

View File

@ -0,0 +1,28 @@
<div class="row border border-2 border-secondary-subtle my-3">
<div class="col-8">
{% if request.user == ticket.user %}
vous avez demandé une critique
{% else %}
{{ ticket.user }} a demandé une critique
{% endif %}
</div>
<div class="col-4 d-flex justify-content-end">
{{ ticket.time_created }}
</div>
<p> {{ ticket.title }} </p>
<p> {{ ticket.body }} </p>
<div class="col-3 mb-2">
<img src="{{ ticket.image.url }}" class="img" style="width: 200px; height: auto" alt="Couverture de {{ ticket.title }}">
</div>
<div class="col-4 d-flex justify-content-end mb-2">
{% if request.user == ticket.user %}
<div class="col-3 align-self-end">
<a href="{% url 'ticket-update' ticket.id %}" type="button" class="btn btn-primary">Modifier</a>
</div>
<div class="col-3 align-self-end">
<a href="{% url 'ticket-delete' ticket.id %}" type="button" class="btn btn-danger">Supprimer</a>
</div>
{% endif %}
</div>
</div>

View File

@ -1,13 +1,6 @@
<form method='post' enctype='multipart/form-data'>
{% csrf_token %}
<label for='title'> Titre: </label>
<div class="d-flex justify-content-center mb-4">{{ ticket_form.title }}</div>
<label for='body'>Description: </label>
<div class="d-flex justify-content-center mb-4">{{ ticket_form.body }}</div>
<label for='image'>Image: </label>
<div class="d-flex justify-content-center mb-4">{{ ticket_form.image }}</div>
<div class="d-flex justify-content-end">
<button type='submit' class='btn btn-primary ml-auto'> Envoyer</button>
</div>
</form>
<label for='title'> Titre: </label>
<div class="d-flex justify-content-center mb-4">{{ ticket_form.title }}</div>
<label for='desc'>Description: </label>
<div class="d-flex justify-content-center mb-4">{{ ticket_form.desc }}</div>
<label for='image'>Image: </label>
<div class="d-flex justify-content-center mb-4">{{ ticket_form.image }}</div>

View File

@ -1,7 +1,8 @@
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from reviews.models import Ticket
from reviews.forms import TicketForm
from django.contrib.auth.decorators import login_required, permission_required
from reviews.models import Ticket, Review
from reviews.forms import TicketForm, ReviewForm
@login_required
def home(request):
@ -10,11 +11,21 @@ def home(request):
@login_required
def flux(request):
return render(request, 'reviews/flux.html')
tickets = Ticket.objects.all()
reviews = Review.objects.all()
context = {
'tickets': tickets,
'reviews': reviews,
}
return render(request, 'reviews/flux.html', context)
@login_required
def posts(request):
return render(request, 'reviews/posts.html')
tickets = Ticket.objects.filter(user=request.user)
reviews = Review.objects.filter(user=request.user)
return render(request,
'reviews/posts.html',
{'tickets': tickets, 'reviews': reviews})
@login_required
def subscribed(request):
@ -43,6 +54,7 @@ def create_ticket(request):
context = {'ticket_form': ticket_form, 'tickets': tickets})
@login_required
@permission_required('review.change_ticket', raise_exception=True)
def update_ticket(request, ticket_id):
ticket = Ticket.objects.get(id=ticket_id)
if request.method == 'POST':
@ -58,6 +70,7 @@ def update_ticket(request, ticket_id):
{'ticket_form': ticket_form})
@login_required
@permission_required('review.onwer', raise_exception=True)
def delete_ticket(request, ticket_id):
ticket = Ticket.objects.get(id=ticket_id)
if request.method == 'POST':
@ -68,17 +81,90 @@ def delete_ticket(request, ticket_id):
'reviews/ticket_delete.html',
{'ticket': ticket})
def review(request):
pass
@login_required
def review(request, review_id):
review = Review.objects.get(id=review_id)
ticket = review.ticket
return render(request,
'reviews/review.html',
{'review': review})
@login_required
def create_review(request):
pass
ticket_form = TicketForm()
review_form = ReviewForm()
if request.method == 'POST':
ticket_form = TicketForm(request.POST, request.FILES)
review_form = ReviewForm(request.POST)
print(request.POST)
def update_review(request):
pass
if all([ticket_form.is_valid(), review_form.is_valid()]):
ticket = ticket_form.save(commit=False)
print(ticket)
ticket.user = request.user
ticket.save()
review = review_form.save(commit=False)
print(review)
review.user = request.user
review.ticket = ticket
review.save()
return redirect('posts')
def delete_review(request):
pass
context = {
'ticket_form': ticket_form,
'review_form': review_form,
}
return render(request,
'reviews/review_create.html', context)
@login_required
def ticket_review(request, ticket_id):
ticket = Ticket.objects.get(id=ticket_id)
review_form = ReviewForm()
if request.method == 'POST':
review_form = ReviewForm(request.POST)
print(request.POST)
if review_form.is_valid():
review = review_form.save(commit=False)
review.user = request.user
review.ticket = ticket
review.save()
return redirect('posts')
context = {
'ticket': ticket,
'review_form': review_form,
}
return render(request,
'reviews/review_ticket.html', context)
@login_required
def update_review(request, review_id):
review = Review.objects.get(id=review_id)
if request.method == 'POST':
print(request.POST)
review_form = ReviewForm(request.POST, instance=review)
print(review_form.is_valid())
if review_form.is_valid():
review = review_form.save()
return redirect('home')
else:
review_form = ReviewForm(instance=review)
return render(request,
'reviews/review_update.html',
{'review_form': review_form, 'review': review})
@login_required
@permission_required('review.owner', raise_exception=True)
def delete_review(request, review_id):
review = Review.objects.get(id=review_id)
if request.method == 'POST':
review.delete()
return redirect('posts')
return render(request,
'reviews/review_delete.html',
{'review': review})
def follow_user(request):
pass

View File

@ -15,7 +15,9 @@
<div class="col d-flex justify-content-center">
<h1> LITReview </h1>
</div>
{% if user.is_authenticated %}
{% include 'nav.html' %}
{% endif %}
</div>
</nav>
</header>

View File

@ -1,4 +1,3 @@
{% if user.is_authenticated %}
<ul class="navbar-nav z-index=2">
<li class="nav-item">
<a href="{% url 'home' %}" class="nav-link {% if navbar == 'home' %}active{% endif %}">Flux</a>
@ -14,5 +13,4 @@
<button type="submit" class="btn btn-light">Se déconnecter</button>
</form>
</ul>
{% endif %}