@@ -1,3 +1,167 @@ | |||
.vscode/ | |||
# Created by https://www.toptal.com/developers/gitignore/api/django | |||
# Edit at https://www.toptal.com/developers/gitignore?templates=django | |||
### Django ### | |||
*.log | |||
*.pot | |||
*.pyc | |||
__pycache__/ | |||
local_settings.py | |||
db.sqlite3 | |||
db.sqlite3-journal | |||
media | |||
# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ | |||
# in your Git repository. Update and uncomment the following line accordingly. | |||
# <django-project-name>/staticfiles/ | |||
### Django.Python Stack ### | |||
# Byte-compiled / optimized / DLL files | |||
*.py[cod] | |||
*$py.class | |||
# C extensions | |||
*.so | |||
# Distribution / packaging | |||
.Python | |||
build/ | |||
develop-eggs/ | |||
dist/ | |||
downloads/ | |||
eggs/ | |||
.eggs/ | |||
parts/ | |||
sdist/ | |||
var/ | |||
wheels/ | |||
pip-wheel-metadata/ | |||
share/python-wheels/ | |||
*.egg-info/ | |||
.installed.cfg | |||
*.egg | |||
MANIFEST | |||
# PyInstaller | |||
# Usually these files are written by a python script from a template | |||
# before PyInstaller builds the exe, so as to inject date/other infos into it. | |||
*.manifest | |||
*.spec | |||
# Installer logs | |||
pip-log.txt | |||
pip-delete-this-directory.txt | |||
# Unit test / coverage reports | |||
htmlcov/ | |||
.tox/ | |||
.nox/ | |||
.coverage | |||
.coverage.* | |||
.cache | |||
nosetests.xml | |||
coverage.xml | |||
*.cover | |||
*.py,cover | |||
.hypothesis/ | |||
.pytest_cache/ | |||
pytestdebug.log | |||
# Translations | |||
*.mo | |||
# Django stuff: | |||
# Flask stuff: | |||
instance/ | |||
.webassets-cache | |||
# Scrapy stuff: | |||
.scrapy | |||
# Sphinx documentation | |||
docs/_build/ | |||
doc/_build/ | |||
# PyBuilder | |||
target/ | |||
# Jupyter Notebook | |||
.ipynb_checkpoints | |||
# IPython | |||
profile_default/ | |||
ipython_config.py | |||
# pyenv | |||
.python-version | |||
# pipenv | |||
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. | |||
# However, in case of collaboration, if having platform-specific dependencies or dependencies | |||
# having no cross-platform support, pipenv may install dependencies that don't work, or not | |||
# install all needed dependencies. | |||
#Pipfile.lock | |||
# poetry | |||
#poetry.lock | |||
# PEP 582; used by e.g. github.com/David-OConnor/pyflow | |||
__pypackages__/ | |||
# Celery stuff | |||
celerybeat-schedule | |||
celerybeat.pid | |||
# SageMath parsed files | |||
*.sage.py | |||
# Environments | |||
# .env | |||
.env/ | |||
.venv/ | |||
env/ | |||
venv/ | |||
ENV/ | |||
env.bak/ | |||
venv.bak/ | |||
pythonenv* | |||
# Spyder project settings | |||
.spyderproject | |||
.spyproject | |||
# Rope project settings | |||
.ropeproject | |||
# mkdocs documentation | |||
/site | |||
# mypy | |||
.mypy_cache/ | |||
.dmypy.json | |||
dmypy.json | |||
# Pyre type checker | |||
.pyre/ | |||
# pytype static type analyzer | |||
.pytype/ | |||
# operating system-related files | |||
# file properties cache/storage on macOS | |||
*.DS_Store | |||
# thumbnail cache on Windows | |||
Thumbs.db | |||
# profiling data | |||
.prof | |||
# End of https://www.toptal.com/developers/gitignore/api/django | |||
# ---> Python | |||
# Byte-compiled / optimized / DLL files | |||
__pycache__/ | |||
@@ -0,0 +1,22 @@ | |||
#!/usr/bin/env python | |||
"""Django's command-line utility for administrative tasks.""" | |||
import os | |||
import sys | |||
def main(): | |||
"""Run administrative tasks.""" | |||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'site_ecole.settings') | |||
try: | |||
from django.core.management import execute_from_command_line | |||
except ImportError as exc: | |||
raise ImportError( | |||
"Couldn't import Django. Are you sure it's installed and " | |||
"available on your PYTHONPATH environment variable? Did you " | |||
"forget to activate a virtual environment?" | |||
) from exc | |||
execute_from_command_line(sys.argv) | |||
if __name__ == '__main__': | |||
main() |
@@ -0,0 +1,28 @@ | |||
from .models import Evenement, Plat | |||
from django.contrib import admin | |||
class PlatInline(admin.TabularInline): | |||
model = Plat | |||
extra = 1 | |||
class EvenementAdmin(admin.ModelAdmin): | |||
fieldsets = ( | |||
("Description", { | |||
"fields": ( | |||
"nom_evenement", | |||
), | |||
}), | |||
("Détail", { | |||
"fields": ( | |||
"date_evenement", | |||
), | |||
"classes": ( | |||
"collapse", | |||
) | |||
}) | |||
) | |||
inlines = (PlatInline,) | |||
list_display = ("nom_evenement", "date_evenement", "futur") | |||
# Register your models here. | |||
admin.site.register(Evenement, EvenementAdmin) |
@@ -0,0 +1,6 @@ | |||
from django.apps import AppConfig | |||
class RepasConfig(AppConfig): | |||
default_auto_field = 'django.db.models.BigAutoField' | |||
name = 'repas' |
@@ -0,0 +1,32 @@ | |||
# Generated by Django 3.2.3 on 2021-06-01 07:11 | |||
from django.db import migrations, models | |||
import django.db.models.deletion | |||
class Migration(migrations.Migration): | |||
initial = True | |||
dependencies = [ | |||
] | |||
operations = [ | |||
migrations.CreateModel( | |||
name='Evenement', | |||
fields=[ | |||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | |||
('nom_evenement', models.CharField(max_length=200, verbose_name='évènement')), | |||
('date_evenement', models.DateTimeField(verbose_name="date de l'évènement")), | |||
], | |||
), | |||
migrations.CreateModel( | |||
name='Repas', | |||
fields=[ | |||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | |||
('nom_repas', models.CharField(max_length=200, verbose_name='nom du repas')), | |||
('commandes', models.IntegerField(default=0, verbose_name='quantité commandées')), | |||
('evenement', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repas.evenement', verbose_name='évènement')), | |||
], | |||
), | |||
] |
@@ -0,0 +1,24 @@ | |||
# Generated by Django 3.2.3 on 2021-06-01 07:40 | |||
from django.db import migrations, models | |||
import django.utils.timezone | |||
class Migration(migrations.Migration): | |||
dependencies = [ | |||
('repas', '0001_initial'), | |||
] | |||
operations = [ | |||
migrations.AddField( | |||
model_name='repas', | |||
name='prix_repas', | |||
field=models.FloatField(default=0, verbose_name='prix'), | |||
), | |||
migrations.AlterField( | |||
model_name='evenement', | |||
name='date_evenement', | |||
field=models.DateTimeField(default=django.utils.timezone.now, verbose_name="date de l'évènement"), | |||
), | |||
] |
@@ -0,0 +1,27 @@ | |||
# Generated by Django 3.2.3 on 2021-06-01 08:11 | |||
from django.db import migrations, models | |||
import django.db.models.deletion | |||
class Migration(migrations.Migration): | |||
dependencies = [ | |||
('repas', '0002_auto_20210601_0940'), | |||
] | |||
operations = [ | |||
migrations.CreateModel( | |||
name='Plat', | |||
fields=[ | |||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), | |||
('nom_plat', models.CharField(max_length=200, verbose_name='nom du plat')), | |||
('prix_plat', models.FloatField(default=0, verbose_name='prix')), | |||
('commandes', models.IntegerField(default=0, verbose_name='quantité commandées')), | |||
('evenement', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='repas.evenement', verbose_name='évènement')), | |||
], | |||
), | |||
migrations.DeleteModel( | |||
name='Repas', | |||
), | |||
] |
@@ -0,0 +1,30 @@ | |||
from django.db import models | |||
from django.utils import timezone | |||
from django.contrib import admin | |||
class Evenement(models.Model): | |||
nom_evenement = models.CharField("évènement", max_length=200) | |||
date_evenement = models.DateTimeField("date de l'évènement", default=timezone.now) | |||
def __str__(self): | |||
return self.nom_evenement | |||
@admin.display( | |||
boolean=True, | |||
ordering="date_evenement", | |||
description="à venir" | |||
) | |||
def futur(self): | |||
return self.date_evenement > timezone.now() | |||
class Plat(models.Model): | |||
evenement = models.ForeignKey(Evenement, verbose_name="évènement", on_delete=models.CASCADE) | |||
nom_plat = models.CharField("nom du plat", max_length=200) | |||
prix_plat = models.FloatField("prix", default=0) | |||
commandes = models.IntegerField("quantité commandées", default=0) | |||
def __str__(self): | |||
return self.nom_plat | |||
def prix_total(self): | |||
return self.commandes * self.prix_plat |
@@ -0,0 +1,12 @@ | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> | |||
<meta charset="UTF-8"> | |||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> | |||
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |||
<title>{% block title %}{% endblock %}</title> | |||
</head> | |||
<body> | |||
{% block content %}{% endblock %} | |||
</body> | |||
</html> |
@@ -0,0 +1,15 @@ | |||
{% extends 'base.html' %} | |||
{% block title %}Merci !{% endblock title %} | |||
{% block content %} | |||
<h1>Merci d'avoir commandé</h1> | |||
<p>Résumé des commandes</p> | |||
<table> | |||
{% for plat in evenement.plat_set.all %} | |||
<tr> | |||
<td>{{plat.commandes}}</td> | |||
<td> x {{ plat }}</td> | |||
<td> = {{ plat.prix_total }} € </td> | |||
</tr> | |||
{% endfor %} | |||
</table> | |||
{% endblock content %} |
@@ -0,0 +1,20 @@ | |||
{% extends 'base.html' %} | |||
{% block title %}{{ evenement }}{% endblock title %} | |||
{% block content %} | |||
<h1>Choix de plats</h1> | |||
<form action="{% url 'repas:commande' evenement.id %}" method="post"> | |||
{% csrf_token %} | |||
<ul> | |||
{% for plat in evenement.plat_set.all %} | |||
<li> | |||
<input type="number" style="width:40px" name="{{plat.nom_plat}}" id="plat{{plat.id}}"> x | |||
<label for="plat{{plat.id}}">{{ plat }} ({{plat.prix_plat}} €)</label> | |||
<br> | |||
</li> | |||
{% endfor %} | |||
</ul> | |||
<input type="submit" value="Valider"> | |||
</form> | |||
{% endblock content %} | |||
@@ -0,0 +1,13 @@ | |||
{% extends 'base.html' %} | |||
{% block title %}Commande des Plat - Accueil{% endblock title %} | |||
{% block content %} | |||
<h1>Commande des repas</h1> | |||
<ul> | |||
{% for evenement in evenements_futurs %} | |||
<li><a href={% url 'repas:evenement' evenement.pk %}>{{ evenement }}</a></li> | |||
{% endfor %} | |||
</ul> | |||
{% endblock content %} | |||
@@ -0,0 +1,3 @@ | |||
from django.test import TestCase | |||
# Create your tests here. |
@@ -0,0 +1,11 @@ | |||
from django.urls import path | |||
from . import views | |||
app_name = "repas" | |||
urlpatterns = [ | |||
path('', views.index, name="index"), | |||
path('<int:id_evenement>/', views.detail_evenement, name='evenement'), | |||
path('<int:id_evenement>/commande', views.commande, name='commande'), | |||
path('<int:id_evenement>/commandes', views.commandes, name='commandes'), | |||
] |
@@ -0,0 +1,30 @@ | |||
from django.http.response import HttpResponseRedirect | |||
from django.shortcuts import get_object_or_404, render | |||
from django.urls import reverse | |||
from django.utils import timezone | |||
from .models import Evenement, Plat | |||
# Create your views here. | |||
def index(request): | |||
evenements_futurs = Evenement.objects.filter(date_evenement__gte=timezone.now()) | |||
contexte = { | |||
"evenements_futurs": evenements_futurs | |||
} | |||
return render(request, "repas/index.html", contexte) | |||
def detail_evenement(request, id_evenement): | |||
evenement = get_object_or_404(Evenement, pk=id_evenement) | |||
return render(request, 'repas/evenement.html', {"evenement":evenement}) | |||
def commande(request, id_evenement): | |||
evenement = get_object_or_404(Evenement, pk=id_evenement) | |||
for plat in evenement.plat_set.all(): | |||
nombre_plat = request.POST[plat.nom_plat] | |||
if nombre_plat == "": nombre_plat = 0 | |||
plat.commandes += int(nombre_plat) | |||
plat.save() | |||
return HttpResponseRedirect(reverse('repas:commandes', args=(id_evenement,))) | |||
def commandes(request, id_evenement): | |||
evenement = get_object_or_404(Evenement, pk=id_evenement) | |||
return render(request, 'repas/commandes.html', {'evenement':evenement}) |
@@ -0,0 +1,16 @@ | |||
""" | |||
ASGI config for site_ecole project. | |||
It exposes the ASGI callable as a module-level variable named ``application``. | |||
For more information on this file, see | |||
https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ | |||
""" | |||
import os | |||
from django.core.asgi import get_asgi_application | |||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'site_ecole.settings') | |||
application = get_asgi_application() |
@@ -0,0 +1,126 @@ | |||
""" | |||
Django settings for site_ecole project. | |||
Generated by 'django-admin startproject' using Django 3.2.3. | |||
For more information on this file, see | |||
https://docs.djangoproject.com/en/3.2/topics/settings/ | |||
For the full list of settings and their values, see | |||
https://docs.djangoproject.com/en/3.2/ref/settings/ | |||
""" | |||
from pathlib import Path | |||
# Build paths inside the project like this: BASE_DIR / 'subdir'. | |||
BASE_DIR = Path(__file__).resolve().parent.parent | |||
# Quick-start development settings - unsuitable for production | |||
# See https://docs.djangoproject.com/en/3.2/howto/deployment/checklist/ | |||
# SECURITY WARNING: keep the secret key used in production secret! | |||
SECRET_KEY = 'django-insecure-joweoexf@ru5685jzoxmqwi#*0=nkezctf6p^g21(jizp4_%9j' | |||
# SECURITY WARNING: don't run with debug turned on in production! | |||
DEBUG = True | |||
ALLOWED_HOSTS = [] | |||
# Application definition | |||
INSTALLED_APPS = [ | |||
'repas.apps.RepasConfig', | |||
'django.contrib.admin', | |||
'django.contrib.auth', | |||
'django.contrib.contenttypes', | |||
'django.contrib.sessions', | |||
'django.contrib.messages', | |||
'django.contrib.staticfiles', | |||
] | |||
MIDDLEWARE = [ | |||
'django.middleware.security.SecurityMiddleware', | |||
'django.contrib.sessions.middleware.SessionMiddleware', | |||
'django.middleware.common.CommonMiddleware', | |||
'django.middleware.csrf.CsrfViewMiddleware', | |||
'django.contrib.auth.middleware.AuthenticationMiddleware', | |||
'django.contrib.messages.middleware.MessageMiddleware', | |||
'django.middleware.clickjacking.XFrameOptionsMiddleware', | |||
] | |||
ROOT_URLCONF = 'site_ecole.urls' | |||
TEMPLATES = [ | |||
{ | |||
'BACKEND': 'django.template.backends.django.DjangoTemplates', | |||
'DIRS': [], | |||
'APP_DIRS': True, | |||
'OPTIONS': { | |||
'context_processors': [ | |||
'django.template.context_processors.debug', | |||
'django.template.context_processors.request', | |||
'django.contrib.auth.context_processors.auth', | |||
'django.contrib.messages.context_processors.messages', | |||
], | |||
}, | |||
}, | |||
] | |||
WSGI_APPLICATION = 'site_ecole.wsgi.application' | |||
# Database | |||
# https://docs.djangoproject.com/en/3.2/ref/settings/#databases | |||
DATABASES = { | |||
'default': { | |||
'ENGINE': 'django.db.backends.sqlite3', | |||
'NAME': BASE_DIR / 'db.sqlite3', | |||
} | |||
} | |||
# Password validation | |||
# https://docs.djangoproject.com/en/3.2/ref/settings/#auth-password-validators | |||
AUTH_PASSWORD_VALIDATORS = [ | |||
{ | |||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', | |||
}, | |||
{ | |||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', | |||
}, | |||
{ | |||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', | |||
}, | |||
{ | |||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', | |||
}, | |||
] | |||
# Internationalization | |||
# https://docs.djangoproject.com/en/3.2/topics/i18n/ | |||
LANGUAGE_CODE = 'fr-fr' | |||
TIME_ZONE = 'Europe/Brussels' | |||
USE_I18N = True | |||
USE_L10N = True | |||
USE_TZ = True | |||
# Static files (CSS, JavaScript, Images) | |||
# https://docs.djangoproject.com/en/3.2/howto/static-files/ | |||
STATIC_URL = '/static/' | |||
# Default primary key field type | |||
# https://docs.djangoproject.com/en/3.2/ref/settings/#default-auto-field | |||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' |
@@ -0,0 +1,7 @@ | |||
from django.contrib import admin | |||
from django.urls import path, include | |||
urlpatterns = [ | |||
path('', include("repas.urls")), | |||
path('admin/', admin.site.urls), | |||
] |
@@ -0,0 +1,16 @@ | |||
""" | |||
WSGI config for site_ecole project. | |||
It exposes the WSGI callable as a module-level variable named ``application``. | |||
For more information on this file, see | |||
https://docs.djangoproject.com/en/3.2/howto/deployment/wsgi/ | |||
""" | |||
import os | |||
from django.core.wsgi import get_wsgi_application | |||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'site_ecole.settings') | |||
application = get_wsgi_application() |