Merge branch 'authentication' into 'develop'

Authentification

See merge request hashbangfr/la_chariotte!6
This commit is contained in:
Laetitia Getti 2023-04-27 13:31:52 +00:00
commit 0c7f489331
26 changed files with 359 additions and 38 deletions

View file

@ -33,3 +33,10 @@ Si il y a des erreurs BLACK, on peut lancer black pour linter le code :
```bash ```bash
black . black .
``` ```
## Architecture de l'application
Les différentes applications Django créées sont :
- Order, pour gérer tout ce qui tourne autour des commandes
- Accounts, pour gérer la création de comptes. Pour la connexion, la déconnexion et le changement de mot de passe, on utilise l'application auth intégrée à Django.

3
conftest.py Normal file
View file

@ -0,0 +1,3 @@
pytest_plugins = [
"la_chariotte.helpers.fixtures",
]

View file

@ -12,6 +12,12 @@ black==23.3.0
# via pytest-black # via pytest-black
build==0.10.0 build==0.10.0
# via pip-tools # via pip-tools
chardet==4.0.0
# via diff-cover
# via flake8-black
build==0.10.0
# via pip-tools
# via pytest-black
chardet==4.0.0 chardet==4.0.0
# via diff-cover # via diff-cover
click==8.1.3 click==8.1.3
@ -32,6 +38,16 @@ inflect==3.0.2
# via # via
# diff-cover # diff-cover
# jinja2-pluralize # jinja2-pluralize
flake8==6.0.0
# via flake8-black
flake8-black==0.3.6
# via la-chariotte (pyproject.toml)
importlib-metadata==6.1.0
# via inflect
inflect==3.0.2
# via
# diff-cover
# jinja2-pluralize
iniconfig==2.0.0 iniconfig==2.0.0
# via pytest # via pytest
isort==5.12.0 isort==5.12.0
@ -44,6 +60,8 @@ jinja2-pluralize==0.3.0
# via diff-cover # via diff-cover
markupsafe==2.1.2 markupsafe==2.1.2
# via jinja2 # via jinja2
mccabe==0.7.0
# via flake8
mypy-extensions==1.0.0 mypy-extensions==1.0.0
# via black # via black
packaging==23.0 packaging==23.0
@ -63,6 +81,13 @@ pluggy==1.0.0
# pytest # pytest
psycopg2-binary==2.9.6 psycopg2-binary==2.9.6
# via la-chariotte (pyproject.toml) # via la-chariotte (pyproject.toml)
pygments==2.14.0
# via diff-cover
# via la-chariotte (pyproject.toml)
pycodestyle==2.10.0
# via flake8
pyflakes==3.0.1
# via flake8
pygments==2.14.0 pygments==2.14.0
# via diff-cover # via diff-cover
pyproject-hooks==1.0.0 pyproject-hooks==1.0.0
@ -95,6 +120,14 @@ tomli==2.0.1
# pytest # pytest
wheel==0.40.0 wheel==0.40.0
# via pip-tools # via pip-tools
zipp==3.15.0
# via importlib-metadata
# coverage
# flake8-black
# pyproject-hooks
# pytest
wheel==0.40.0
# via pip-tools
zipp==3.15.0 zipp==3.15.0
# via importlib-metadata # via importlib-metadata

View file

View file

@ -0,0 +1,6 @@
from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "la_chariotte.accounts"

View file

@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

View file

@ -0,0 +1,14 @@
{% extends 'base.html' %}
{% block title %}Connexion{% endblock %}
{% block content %}
<h2>Connexion</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Se connecter</button>
<a href="{% url 'accounts:signup' %}">Créer un compte</button>
</form>
{% endblock %}

View file

@ -0,0 +1,13 @@
{% extends 'base.html' %}
{% block title %}Créer un compte{% endblock %}
{% block content %}
<h2>Créer un compte</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Valider</button>
</form>
{% endblock %}

View file

@ -0,0 +1,9 @@
from django.urls import include, path
from . import views
app_name = "accounts"
urlpatterns = [
path("inscription/", views.SignUpView.as_view(), name="signup"),
path("", include("django.contrib.auth.urls")),
]

View file

@ -0,0 +1,9 @@
from django.contrib.auth.forms import UserCreationForm
from django.urls import reverse_lazy
from django.views import generic
class SignUpView(generic.CreateView):
form_class = UserCreationForm
success_url = reverse_lazy("accounts:login")
template_name = "registration/signup.html"

View file

@ -0,0 +1,18 @@
import pytest
@pytest.fixture
def client_log(client, django_user_model):
username = "test@user.fr"
password = "azertypassword"
user = django_user_model.objects.create_user(username=username, password=password)
client.login(username=username, password=password)
return client
@pytest.fixture
def other_user(django_user_model):
username = "other@user.fr"
password = "azertypassword"
user = django_user_model.objects.create_user(username=username, password=password)
return user

View file

@ -0,0 +1,22 @@
# Generated by Django 4.2 on 2023-04-18 13:08
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("order", "0010_rename_grouped_order_groupedorder"),
]
operations = [
migrations.AlterField(
model_name="groupedorder",
name="orga",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL
),
),
]

View file

@ -1,10 +1,15 @@
from django.contrib.auth.models import User
from django.db import models from django.db import models
from django.utils import timezone from django.utils import timezone
class GroupedOrder(models.Model): class GroupedOrder(models.Model):
name = models.CharField(max_length=100, null=True) # optionnal name = models.CharField(
orga = models.CharField(max_length=100) # a changer, utiliser ForeignKey de user max_length=100, null=True, verbose_name="Titre de la commande"
) # optionnal
orga = models.ForeignKey(
User, on_delete=models.CASCADE, verbose_name="Organisateur·ice"
)
delivery_date = models.DateField("Date de livraison") delivery_date = models.DateField("Date de livraison")
deadline = models.DateTimeField("Date limite de commande") deadline = models.DateTimeField("Date limite de commande")

View file

@ -11,7 +11,19 @@
<p>Organisateur·ice : {{ grouped_order.orga }}</p> <p>Organisateur·ice : {{ grouped_order.orga }}</p>
<p>Date de livraison : {{ grouped_order.delivery_date }}</p> <p>Date de livraison : {{ grouped_order.delivery_date }}</p>
les produits disponibles pour cette commande groupée : {% if not user.is_authenticated %}
<p>Vous êtes l'organisateur·ice de cette commande groupée ?
<a href="{% url 'order:grouped_order_overview' grouped_order.id %}">
Connectez-vous pour accéder à la page de gestion</a>
</p>
{% endif %}
{% if user == grouped_order.orga %}
<a href="{% url 'order:grouped_order_overview' grouped_order.id %}">
Page de gestion de la commande groupée</a>
{% endif %}
<p>les produits disponibles pour cette commande groupée : </p>
<ul> <ul>
{% for item in grouped_order.item_set.all %} {% for item in grouped_order.item_set.all %}

View file

@ -29,6 +29,6 @@
{% endfor %} {% endfor %}
</ul> </ul>
<a href={% url 'order:order' grouped_order.id %}>Retour à la page de commande</a> <a href={% url 'order:grouped_order_detail' grouped_order.id %}>Retour à la page de commande</a>
</body> </body>
</html> </html>

View file

@ -6,7 +6,6 @@
</head> </head>
<body> <body>
<p>Index des commandes que l'utilisateur·ice connecté·e organise</p> <p>Index des commandes que l'utilisateur·ice connecté·e organise</p>
<p>Pour l'instant, index de toutes les commandes groupées qui existent</p>
{% if grouped_order_list.incoming_grouped_orders or grouped_order_list.crossed_deadline_grouped_orders or grouped_order_list.old_grouped_orders %} {% if grouped_order_list.incoming_grouped_orders or grouped_order_list.crossed_deadline_grouped_orders or grouped_order_list.old_grouped_orders %}
{% if grouped_order_list.incoming_grouped_orders %} {% if grouped_order_list.incoming_grouped_orders %}

View file

@ -13,6 +13,7 @@ class TestGroupedOrdersModel:
is_ongoing() returns True if the deadline is not crossed is_ongoing() returns True if the deadline is not crossed
""" """
deadline = timezone.now() + datetime.timedelta(days=10) deadline = timezone.now() + datetime.timedelta(days=10)
ongoing_gr_order = GroupedOrder(deadline=deadline) ongoing_gr_order = GroupedOrder(deadline=deadline)
assert ongoing_gr_order.is_ongoing() assert ongoing_gr_order.is_ongoing()

View file

@ -1,6 +1,8 @@
import datetime import datetime
import pytest import pytest
from django.contrib import auth
from django.contrib.auth.models import User
from django.urls import reverse from django.urls import reverse
from django.utils import timezone from django.utils import timezone
@ -9,23 +11,35 @@ from la_chariotte.order.models import GroupedOrder, Item, Order
pytestmark = pytest.mark.django_db pytestmark = pytest.mark.django_db
def create_grouped_order(days_before_delivery_date, days_before_deadline, name): def create_grouped_order(
days_before_delivery_date, days_before_deadline, name, orga_user
):
""" """
Creates a grouped order. Creates a grouped order.
""" """
date = timezone.now().date() + datetime.timedelta(days=days_before_delivery_date) date = timezone.now().date() + datetime.timedelta(days=days_before_delivery_date)
deadline = timezone.now() + datetime.timedelta(days=days_before_deadline) deadline = timezone.now() + datetime.timedelta(days=days_before_deadline)
return GroupedOrder.objects.create( return GroupedOrder.objects.create(
name=name, orga="test orga", delivery_date=date, deadline=deadline name=name, orga=orga_user, delivery_date=date, deadline=deadline
) )
class TestGroupedOrderIndexView: class TestGroupedOrderIndexView:
def test_no_grouped_orders(self, client): def test_anonymous_user_redirection(self, client):
"""
If the user is anonymous, they are redirected to login view
"""
assert auth.get_user(client).is_anonymous
response = client.get(reverse("order:index"))
assert response.status_code == 302
assert response.url.startswith(reverse("accounts:login"))
assert response.url.endswith(reverse("order:index"))
def test_no_grouped_orders(self, client_log):
""" """
If no grouped order exist, an appropriate message is displayed If no grouped order exist, an appropriate message is displayed
""" """
response = client.get(reverse("order:index")) response = client_log.get(reverse("order:index"))
assert response.status_code == 200 assert response.status_code == 200
assert "Pas de commande groupée pour l'instant" in response.content.decode() assert "Pas de commande groupée pour l'instant" in response.content.decode()
assert len(response.context["grouped_order_list"]["old_grouped_orders"]) == 0 assert len(response.context["grouped_order_list"]["old_grouped_orders"]) == 0
@ -41,22 +55,29 @@ class TestGroupedOrderIndexView:
len(response.context["grouped_order_list"]["incoming_grouped_orders"]) == 0 len(response.context["grouped_order_list"]["incoming_grouped_orders"]) == 0
) )
def test_grouped_orders_in_right_section(self, client): def test_grouped_orders_in_right_section(self, client_log):
""" """
According to their delivery date and deadline, grouped orders are placed in the correct section : several gr orders According to their delivery date and deadline, grouped orders are placed in the correct section : several gr orders
""" """
future_grouped_order = create_grouped_order( future_grouped_order = create_grouped_order(
days_before_delivery_date=5, days_before_deadline=2, name="future" days_before_delivery_date=5,
days_before_deadline=2,
name="future",
orga_user=auth.get_user(client_log),
) )
crossed_deadline_gr_order = create_grouped_order( crossed_deadline_gr_order = create_grouped_order(
days_before_delivery_date=2, days_before_delivery_date=2,
days_before_deadline=-1, days_before_deadline=-1,
name="crossed deadline", name="crossed deadline",
orga_user=auth.get_user(client_log),
) )
old_gr_order = create_grouped_order( old_gr_order = create_grouped_order(
days_before_delivery_date=-1, days_before_deadline=-3, name="old" days_before_delivery_date=-1,
days_before_deadline=-3,
name="old",
orga_user=auth.get_user(client_log),
) )
response = client.get(reverse("order:index")) response = client_log.get(reverse("order:index"))
assert response.status_code == 200 assert response.status_code == 200
assert "Pas de commande groupée pour l'instant" not in response.content.decode() assert "Pas de commande groupée pour l'instant" not in response.content.decode()
assert "Commandes groupées à venir" in response.content.decode() assert "Commandes groupées à venir" in response.content.decode()
@ -87,14 +108,17 @@ class TestGroupedOrderIndexView:
== future_grouped_order == future_grouped_order
) )
def test_grouped_orders_in_right_section__with_only_old(self, client): def test_grouped_orders_in_right_section__with_only_old(self, client_log):
""" """
According to their delivery date and deadline, grouped orders are placed in correct section : only old gr order According to their delivery date and deadline, grouped orders are placed in correct section : only old gr order
""" """
old_gr_order = create_grouped_order( old_gr_order = create_grouped_order(
days_before_delivery_date=-1, days_before_deadline=-3, name="passée" days_before_delivery_date=-1,
days_before_deadline=-3,
name="passée",
orga_user=auth.get_user(client_log),
) )
response = client.get(reverse("order:index")) response = client_log.get(reverse("order:index"))
assert response.status_code == 200 assert response.status_code == 200
assert "Pas de commande groupée pour l'instant" not in response.content.decode() assert "Pas de commande groupée pour l'instant" not in response.content.decode()
assert "Commandes groupées à venir" not in response.content.decode() assert "Commandes groupées à venir" not in response.content.decode()
@ -117,14 +141,17 @@ class TestGroupedOrderIndexView:
== old_gr_order == old_gr_order
) )
def test_grouped_orders_in_right_section__with_only_future(self, client): def test_grouped_orders_in_right_section__with_only_future(self, client_log):
""" """
According to their delivery date and deadline, grouped orders are placed in correct section : only incoming gr order According to their delivery date and deadline, grouped orders are placed in correct section : only incoming gr order
""" """
future_grouped_order = create_grouped_order( future_grouped_order = create_grouped_order(
days_before_delivery_date=5, days_before_deadline=2, name="future" days_before_delivery_date=5,
days_before_deadline=2,
name="future",
orga_user=auth.get_user(client_log),
) )
response = client.get(reverse("order:index")) response = client_log.get(reverse("order:index"))
assert response.status_code == 200 assert response.status_code == 200
assert "Pas de commande groupée pour l'instant" not in response.content.decode() assert "Pas de commande groupée pour l'instant" not in response.content.decode()
assert "Commandes groupées à venir" in response.content.decode() assert "Commandes groupées à venir" in response.content.decode()
@ -147,14 +174,45 @@ class TestGroupedOrderIndexView:
== future_grouped_order == future_grouped_order
) )
def test_gr_order_not_organized_by_logged_user(self, client_log, other_user):
"""
If the logged user is not the organizer of a grouped order they don't see it on IndexView
"""
logged_user = auth.get_user(client_log)
future_grouped_order = create_grouped_order(
days_before_delivery_date=5,
days_before_deadline=2,
name="future",
orga_user=other_user,
)
assert future_grouped_order.orga is not logged_user
response = client_log.get(reverse("order:index"))
assert response.status_code == 200
assert "Pas de commande groupée pour l'instant" in response.content.decode()
assert len(response.context["grouped_order_list"]["old_grouped_orders"]) == 0
assert (
len(
response.context["grouped_order_list"][
"crossed_deadline_grouped_orders"
]
)
== 0
)
assert (
len(response.context["grouped_order_list"]["incoming_grouped_orders"]) == 0
)
class TestGroupedOrderDetailView: class TestGroupedOrderDetailView:
def test_order_item(self, client): def test_order_item(self, client, other_user):
""" """
From the OrderDetailView, we order an item using the order form, and it creates an Order woth and Ordered_item inside From the OrderDetailView, we order an item using the order form, and it creates an Order woth and Ordered_item inside
""" """
grouped_order = create_grouped_order( grouped_order = create_grouped_order(
days_before_delivery_date=5, days_before_deadline=2, name="gr order test" days_before_delivery_date=5,
days_before_deadline=2,
name="gr order test",
orga_user=other_user,
) )
item = Item.objects.create(name="test item", grouped_order=grouped_order) item = Item.objects.create(name="test item", grouped_order=grouped_order)
detail_url = reverse( detail_url = reverse(
@ -182,7 +240,7 @@ class TestGroupedOrderDetailView:
) )
assert response.status_code == 302 assert response.status_code == 302
assert response.url == reverse( assert response.url == reverse(
"order:grouped_order_orga", "order:grouped_order_overview",
kwargs={ kwargs={
"pk": grouped_order.pk, "pk": grouped_order.pk,
}, },
@ -191,3 +249,60 @@ class TestGroupedOrderDetailView:
assert item.ordered_nb == 1 assert item.ordered_nb == 1
order = Order.objects.first() order = Order.objects.first()
assert order.ordered_items.count() == 1 assert order.ordered_items.count() == 1
class TestGroupedOrderOrgaView:
def test_user_not_logged_redirect(self, client, other_user):
"""
A user that is not logged cannot see the GroupedOrderOrgaView. They get redirected to the login view
"""
grouped_order = create_grouped_order(
days_before_delivery_date=5,
days_before_deadline=2,
name="gr order test",
orga_user=other_user,
)
orga_view_url = reverse(
"order:grouped_order_overview",
kwargs={
"pk": grouped_order.pk,
},
)
assert auth.get_user(client).is_anonymous
response = client.get(orga_view_url)
assert response.status_code == 302
assert response.url.startswith(reverse("accounts:login"))
assert response.url.endswith(
reverse(
"order:grouped_order_overview",
kwargs={
"pk": grouped_order.pk,
},
)
)
def test_user_not_orga_redirect(self, client_log, other_user):
"""
A user that is not orga cannot see the GroupedOrderOrgaView.
They get a 403 forbidden error
"""
grouped_order = create_grouped_order(
days_before_delivery_date=5,
days_before_deadline=2,
name="gr order test",
orga_user=other_user,
)
orga_view_url = reverse(
"order:grouped_order_overview",
kwargs={
"pk": grouped_order.pk,
},
)
detail_view_url = reverse(
"order:grouped_order_detail",
kwargs={
"pk": grouped_order.pk,
},
)
response = client_log.get(orga_view_url)
assert response.status_code == 403

View file

@ -9,7 +9,9 @@ urlpatterns = [
"<int:pk>/", views.GroupedOrderDetailView.as_view(), name="grouped_order_detail" "<int:pk>/", views.GroupedOrderDetailView.as_view(), name="grouped_order_detail"
), ),
path( path(
"<int:pk>/orga", views.GroupedOrderOrgaView.as_view(), name="grouped_order_orga" "<int:pk>/gerer",
views.GroupedOrderOrgaView.as_view(),
name="grouped_order_overview",
), ),
path("<int:grouped_order_id>/commander/", views.order, name="order"), path("<int:grouped_order_id>/commander/", views.order, name="order"),
] ]

View file

@ -1,25 +1,29 @@
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.http import HttpResponseRedirect from django.http import HttpResponseRedirect
from django.shortcuts import get_object_or_404, render from django.shortcuts import get_object_or_404, render
from django.urls import reverse from django.urls import reverse, reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.views import generic from django.views import generic
from .models import GroupedOrder, Item, Order, OrderedItem from .models import GroupedOrder, Item, Order, OrderedItem
class IndexView(generic.ListView): class IndexView(LoginRequiredMixin, generic.ListView):
"""Vue de toutes les commandes groupées existantes - plus tard, de toutes les commandes groupées de l'utilisateur connecté""" """View showing all the grouped orders organized by the authenticated user"""
template_name = "order/index.html" template_name = "order/index.html"
context_object_name = "grouped_order_list" context_object_name = "grouped_order_list"
def get_queryset(self): def get_queryset(self):
"""Only grouped orders organized by logged user"""
logged_user_grouped_orders = GroupedOrder.objects.filter(orga=self.request.user)
"""3 grouped_order status : incoming, crossed_deadline, and old""" """3 grouped_order status : incoming, crossed_deadline, and old"""
now = timezone.now() now = timezone.now()
today = now.date() today = now.date()
"""Return the 5 most recent old grouped orders""" """Return the 5 most recent old grouped orders"""
old_grouped_orders = GroupedOrder.objects.filter( old_grouped_orders = logged_user_grouped_orders.filter(
# is_to_be_delivered=False # is_to_be_delivered=False
delivery_date__lt=today delivery_date__lt=today
).order_by("-delivery_date")[ ).order_by("-delivery_date")[
@ -28,13 +32,13 @@ class IndexView(generic.ListView):
"""Return all grouped orders, that have crossed their ordering deadline but the delivery date is still to come""" """Return all grouped orders, that have crossed their ordering deadline but the delivery date is still to come"""
crossed_dealine_grouped_orders = ( crossed_dealine_grouped_orders = (
GroupedOrder.objects.filter(delivery_date__gte=today) logged_user_grouped_orders.filter(delivery_date__gte=today)
.filter(deadline__lt=now) .filter(deadline__lt=now)
.order_by("-delivery_date") .order_by("-delivery_date")
) # delivery date >= today (not delivered) and deadline < today (we cannot order) ) # delivery date >= today (not delivered) and deadline < today (we cannot order)
"""Return all incoming grouped orders""" """Return all incoming grouped orders"""
incoming_grouped_orders = GroupedOrder.objects.filter( incoming_grouped_orders = logged_user_grouped_orders.filter(
deadline__gte=now deadline__gte=now
).order_by( ).order_by(
"deadline" "deadline"
@ -47,18 +51,23 @@ class IndexView(generic.ListView):
class GroupedOrderDetailView(generic.DetailView): class GroupedOrderDetailView(generic.DetailView):
"""Vue de détail d'une commande groupée - possibilité de commander si elle est en cours""" """Detail view for a grouped order - possibility to order if it is ongoing - No permissions restriction"""
model = GroupedOrder model = GroupedOrder
template_name = "order/grouped_order_detail.html" template_name = "order/grouped_order_detail.html"
context_object_name = "grouped_order" context_object_name = "grouped_order"
class GroupedOrderOrgaView(generic.DetailView): class GroupedOrderOrgaView(UserPassesTestMixin, generic.DetailView):
"""Vue de supervision d'une commande groupée""" """Overview of a grouped order, for the organizer"""
model = GroupedOrder model = GroupedOrder
template_name = "order/grouped_order_orga.html" template_name = "order/grouped_order_overview.html"
context_object_name = "grouped_order"
def test_func(self):
"""Accessible only if the requesting user is the grouped order organizer"""
return self.get_object().orga == self.request.user
def order( def order(
@ -86,7 +95,7 @@ def order(
# with POST data. This prevents data from being posted twice if a # with POST data. This prevents data from being posted twice if a
# user hits the Back button. # user hits the Back button.
return HttpResponseRedirect( return HttpResponseRedirect(
reverse("order:grouped_order_orga", args=(grouped_order.pk,)) reverse("order:grouped_order_overview", args=(grouped_order.pk,))
) )

View file

@ -32,6 +32,7 @@ ALLOWED_HOSTS = []
INSTALLED_APPS = [ INSTALLED_APPS = [
"la_chariotte.order", "la_chariotte.order",
"la_chariotte.accounts",
"django.contrib.admin", "django.contrib.admin",
"django.contrib.auth", "django.contrib.auth",
"django.contrib.contenttypes", "django.contrib.contenttypes",
@ -52,10 +53,14 @@ MIDDLEWARE = [
ROOT_URLCONF = "la_chariotte.urls" ROOT_URLCONF = "la_chariotte.urls"
LOGIN_URL = "accounts:login"
LOGIN_REDIRECT_URL = "home"
LOGOUT_REDIRECT_URL = "home"
TEMPLATES = [ TEMPLATES = [
{ {
"BACKEND": "django.template.backends.django.DjangoTemplates", "BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [], "DIRS": [BASE_DIR / "la_chariotte" / "templates"],
"APP_DIRS": True, "APP_DIRS": True,
"OPTIONS": { "OPTIONS": {
"context_processors": [ "context_processors": [

View file

@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="fr">
<head>
<meta charset="utf-8" />
<title>{% block title %}{% endblock %} - La Chariotte</title>
</head>
<body>
<main>
{% block content %}
{% endblock %}
</main>
</body>
</html>

View file

@ -0,0 +1,19 @@
{% extends 'base.html' %}
{% block title %}Accueil{% endblock %}
{% block content %}
{% if user.is_authenticated %}
Hi {{ user.username }}!
<p>
<a href="{% url 'order:index' %}">Mes commandes groupées</a>
</p>
<p>
<a href="{% url 'accounts:logout' %}">Se déconnecter</a>
</p>
{% else %}
<p>You are not logged in</p>
<a href="{% url 'accounts:login' %}">Se connecter</a>
{% endif %}
{% endblock %}

View file

@ -15,8 +15,11 @@ Including another URLconf
""" """
from django.contrib import admin from django.contrib import admin
from django.urls import include, path from django.urls import include, path
from django.views.generic.base import TemplateView
urlpatterns = [ urlpatterns = [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("commande/", include("la_chariotte.order.urls")), path("commande/", include("la_chariotte.order.urls")),
path("comptes/", include("la_chariotte.accounts.urls")),
path("", TemplateView.as_view(template_name="home.html"), name="home"),
] ]

View file

@ -1,13 +1,14 @@
[project] [project]
name = "la chariotte" name = "la_chariotte"
version = "0.0.1" version = "0.0.1"
description = "Web application for organising grouped orders" description = "Web application for organising grouped orders"
authors = [{name = "Laetitia Getti", email = "laetitia@chariotte.fr"}]
readme = "readMe.md" readme = "readMe.md"
license = {file = "LICENSE"} license = {file = "LICENSE"}
authors = [{name = "Hashbang", email = "support@hashbang.fr"}] classifiers = ["License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)"]
dependencies = [ dependencies = [
"django>=4,<5", "django>=4,<5",
"psycopg2>=2,<3", "psycopg2-binary>=2,<3",
] ]
[build-system] [build-system]