Bläddra i källkod

[feat] some views

Adrien Carteron 3 år sedan
förälder
incheckning
29387273f6

+ 3 - 1
.gitignore

@@ -1,2 +1,4 @@
 *~
-venv
+venv
+*.bak
+migrations

BIN
db.sqlite3


BIN
expenses/__pycache__/admin.cpython-38.pyc


BIN
expenses/__pycache__/forms.cpython-38.pyc


BIN
expenses/__pycache__/models.cpython-38.pyc


BIN
expenses/__pycache__/urls.cpython-38.pyc


BIN
expenses/__pycache__/views.cpython-38.pyc


+ 15 - 4
expenses/admin.py

@@ -5,6 +5,7 @@ from expenses.models import (
     Category,
     Expense,
     MultiplePaymentExepense,
+    LoneExpense,
 )
 
 
@@ -30,7 +31,7 @@ class CategoryAdmin(admin.ModelAdmin):
     date_hierarchy = "created_at"
 
 
-class ExpenseAdmin(admin.ModelAdmin):
+class LoneExpenseAdmin(admin.ModelAdmin):
     list_display = [
         "name",
         "date",
@@ -47,7 +48,7 @@ class ExpenseAdmin(admin.ModelAdmin):
 class MultiplePaymentExepenseAdmin(admin.ModelAdmin):
     list_display = [
         "name",
-        "first_payment_date",
+        "date",
         "amount",
         "number_of_payment",
         "payments",
@@ -57,11 +58,21 @@ class MultiplePaymentExepenseAdmin(admin.ModelAdmin):
         "modified_at",
     ]
     readonly_fields = ["created_at", "modified_at"]
-    date_hierarchy = "first_payment_date"
+    date_hierarchy = "date"
 
 
+class ExpenseAdmin(admin.ModelAdmin):
+    list_display = [
+        "object_id",
+        "content_type",
+        "content_object",
+    ]
+
+
+admin.site.register(Expense, ExpenseAdmin)
+
 admin.site.register(OnlineSource, OnlineSourceAdmin)
 admin.site.register(PhysicalSource, PhysicalSourceAdmin)
 admin.site.register(Category, CategoryAdmin)
-admin.site.register(Expense, ExpenseAdmin)
+admin.site.register(LoneExpense, LoneExpenseAdmin)
 admin.site.register(MultiplePaymentExepense, MultiplePaymentExepenseAdmin)

+ 28 - 1
expenses/forms.py

@@ -2,7 +2,7 @@ from django import forms
 from django.contrib.auth.models import User
 from django.core.exceptions import ValidationError
 
-from expenses.models import Category
+from expenses.models import Category, Expense, MultiplePaymentExepense, LoneExpense
 
 
 class CategoryForm(forms.ModelForm):
@@ -12,3 +12,30 @@ class CategoryForm(forms.ModelForm):
 
     def __init__(self, *args, **kwargs):
         super().__init__(*args, **kwargs)
+
+
+class ExpenseForm(forms.ModelForm):
+    class Meta:
+        model = Expense
+        fields = "__all__"
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+
+class MultiplePaymentExepenseForm(forms.ModelForm):
+    class Meta:
+        model = MultiplePaymentExepense
+        fields = "__all__"
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+
+class LoneExpenseForm(forms.ModelForm):
+    class Meta:
+        model = LoneExpense
+        fields = "__all__"
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)

+ 47 - 38
expenses/migrations/0001_initial.py

@@ -1,4 +1,4 @@
-# Generated by Django 4.1.3 on 2022-11-03 14:53
+# Generated by Django 4.1.3 on 2022-11-04 10:06
 
 from django.db import migrations, models
 import django.db.models.deletion
@@ -8,7 +8,9 @@ class Migration(migrations.Migration):
 
     initial = True
 
-    dependencies = []
+    dependencies = [
+        ("contenttypes", "0002_remove_content_type_name"),
+    ]
 
     operations = [
         migrations.CreateModel(
@@ -44,23 +46,6 @@ class Migration(migrations.Migration):
                 "ordering": ("name", "type"),
             },
         ),
-        migrations.CreateModel(
-            name="RawExpense",
-            fields=[
-                (
-                    "id",
-                    models.BigAutoField(
-                        auto_created=True,
-                        primary_key=True,
-                        serialize=False,
-                        verbose_name="ID",
-                    ),
-                ),
-                ("name", models.CharField(max_length=150)),
-                ("date", models.DateField()),
-                ("amount", models.DecimalField(decimal_places=2, max_digits=10)),
-            ],
-        ),
         migrations.CreateModel(
             name="Source",
             fields=[
@@ -125,7 +110,7 @@ class Migration(migrations.Migration):
             bases=("expenses.source",),
         ),
         migrations.CreateModel(
-            name="MultiplePaymentExepense",
+            name="RawExpense",
             fields=[
                 (
                     "id",
@@ -139,9 +124,8 @@ class Migration(migrations.Migration):
                 ("created_at", models.DateTimeField(auto_now_add=True, null=True)),
                 ("modified_at", models.DateTimeField(auto_now=True)),
                 ("name", models.CharField(max_length=150)),
-                ("first_payment_date", models.DateField()),
+                ("date", models.DateField()),
                 ("amount", models.DecimalField(decimal_places=2, max_digits=10)),
-                ("number_of_payment", models.PositiveIntegerField()),
                 (
                     "category",
                     models.OneToOneField(
@@ -150,14 +134,6 @@ class Migration(migrations.Migration):
                         to="expenses.category",
                     ),
                 ),
-                (
-                    "payments",
-                    models.ForeignKey(
-                        on_delete=django.db.models.deletion.PROTECT,
-                        related_name="multiple_payment_expense",
-                        to="expenses.rawexpense",
-                    ),
-                ),
                 (
                     "source",
                     models.OneToOneField(
@@ -172,21 +148,23 @@ class Migration(migrations.Migration):
             },
         ),
         migrations.CreateModel(
-            name="Expense",
+            name="MultiplePaymentExepense",
             fields=[
                 (
-                    "rawexpense_ptr",
-                    models.OneToOneField(
+                    "id",
+                    models.BigAutoField(
                         auto_created=True,
-                        on_delete=django.db.models.deletion.CASCADE,
-                        parent_link=True,
                         primary_key=True,
                         serialize=False,
-                        to="expenses.rawexpense",
+                        verbose_name="ID",
                     ),
                 ),
                 ("created_at", models.DateTimeField(auto_now_add=True, null=True)),
                 ("modified_at", models.DateTimeField(auto_now=True)),
+                ("name", models.CharField(max_length=150)),
+                ("first_payment_date", models.DateField()),
+                ("amount", models.DecimalField(decimal_places=2, max_digits=10)),
+                ("number_of_payment", models.PositiveIntegerField()),
                 (
                     "category",
                     models.OneToOneField(
@@ -195,6 +173,14 @@ class Migration(migrations.Migration):
                         to="expenses.category",
                     ),
                 ),
+                (
+                    "payments",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.PROTECT,
+                        related_name="multiple_payment_expense",
+                        to="expenses.rawexpense",
+                    ),
+                ),
                 (
                     "source",
                     models.OneToOneField(
@@ -204,11 +190,34 @@ class Migration(migrations.Migration):
                     ),
                 ),
             ],
+            options={
+                "abstract": False,
+            },
+        ),
+        migrations.CreateModel(
+            name="Expense",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("object_id", models.PositiveIntegerField()),
+                (
+                    "content_type",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to="contenttypes.contenttype",
+                    ),
+                ),
+            ],
             options={
                 "verbose_name": "Expense",
                 "verbose_name_plural": "Expenses",
-                "ordering": ("name", "date", "amount"),
             },
-            bases=("expenses.rawexpense", models.Model),
         ),
     ]

BIN
expenses/migrations/__pycache__/0001_initial.cpython-38.pyc


+ 26 - 7
expenses/models.py

@@ -1,4 +1,6 @@
 from django.db import models
+from django.contrib.contenttypes.models import ContentType
+from django.contrib.contenttypes.fields import GenericForeignKey
 
 
 class AuthoringDatesModel(models.Model):
@@ -45,12 +47,12 @@ class Category(AuthoringDatesModel):
 
 
 class MetaExpense(AuthoringDatesModel):
-    category = models.OneToOneField(
+    category = models.ForeignKey(
         Category,
         related_name="%(app_label)s_%(class)s_related",
         on_delete=models.PROTECT,
     )
-    source = models.OneToOneField(
+    source = models.ForeignKey(
         Source, related_name="%(app_label)s_%(class)s_related", on_delete=models.PROTECT
     )
 
@@ -64,22 +66,39 @@ class RawExpense(models.Model):
     amount = models.DecimalField(max_digits=10, decimal_places=2)
 
 
-class Expense(RawExpense, MetaExpense):
-
-    # Surcharge de la manière d'afficher un objet sensor
+class LoneExpense(RawExpense, MetaExpense):
     def __str__(self):
         return self.name
 
+    class Meta:
+        # db_table = 'book' # Permet de personnaliser le nom de la table en BDD
+        verbose_name = "LoneExpense"  # Le nom lisbile du modèle
+        verbose_name_plural = "LoneExpenses"  # Le nom au pluriel du modèle
+        ordering = ("name", "date", "amount")  # Le tri par défaut dans les listes
+
+
+class Expense(models.Model):
+    object_id = models.PositiveIntegerField()
+    content_type = models.ForeignKey(
+        ContentType,
+        related_name="%(app_label)s_%(class)s_related",
+        on_delete=models.CASCADE,
+    )
+    content_object = GenericForeignKey("content_type", "object_id")
+    # Surcharge de la manière d'afficher un objet sensor
+    """ def __str__(self):
+        return self.name """
+
     class Meta:
         # db_table = 'book' # Permet de personnaliser le nom de la table en BDD
         verbose_name = "Expense"  # Le nom lisbile du modèle
         verbose_name_plural = "Expenses"  # Le nom au pluriel du modèle
-        ordering = ("name", "date", "amount")  # Le tri par défaut dans les listes
+        """ ordering = ("name", "date", "amount")  # Le tri par défaut dans les listes """
 
 
 class MultiplePaymentExepense(MetaExpense):
     name = models.CharField(max_length=150)
-    first_payment_date = models.DateField()
+    date = models.DateField()
     amount = models.DecimalField(max_digits=10, decimal_places=2)
     payments = models.ForeignKey(
         RawExpense, related_name="multiple_payment_expense", on_delete=models.PROTECT

+ 4 - 3
expenses/templates/base.html

@@ -6,9 +6,9 @@
     <meta charset="utf-8">
     <meta name="viewport" content="width=device-width, initial-scale=1">
     <title>{% block title %}{% endblock %}</title>
-    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bootswatch/5.2.1/solar/bootstrap.min.css"
-        integrity="sha512-LhpxG+XCtib/q8R0I0rlktQ4CBE+sRoz22WY/71cBjc2srSqNshLXE0fIZQDsAqQoC7/cHjpNAtRx78MiloTAw=="
-        crossorigin="anonymous" referrerpolicy="no-referrer" />
+    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/css/bootstrap.min.css" rel="stylesheet"
+        integrity="sha384-Zenh87qX5JnK2Jl0vWa8Ck2rdkQ2Bzep5IDxbcnCeuOxjzrPF/et3URy9Bv1WTRi" crossorigin="anonymous">
+
     {% block extra_css %}{% endblock %}
 </head>
 
@@ -58,6 +58,7 @@
     <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.2/dist/js/bootstrap.bundle.min.js"
         integrity="sha384-OERcA2EqjJCMA+/3y+gxIOqMEjwtxJY7qPCqsdltbNJuaOe923+mo//f6V8Qbsw3"
         crossorigin="anonymous"></script>
+
     {% block extra_js %}{% endblock %}
 </body>
 

+ 2 - 2
expenses/templates/categories_list.html

@@ -8,9 +8,9 @@
 
 <ul>
     {% for category in categories %}
-    <li>{{ category }}</a></li>
+    <li><a href="{% url 'expenses:category_detail' pk=category.pk %}">{{ category }}</a></li>
     {% endfor %}
 </ul>
-
+<a class="btn btn-info" href="{% url 'expenses:category_create' %}">Create category</a>
 
 {% endblock content %}

+ 14 - 0
expenses/templates/category_create.html

@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+
+
+{% block content %}
+{% load django_bootstrap5 %}
+
+{% block page_title %}Create category{% endblock %}
+
+<form action="" method="post">
+    {% csrf_token %}
+    {% bootstrap_form form %}
+    {% bootstrap_button button_type="submit" content="Submit" %}
+</form>
+{% endblock content %}

+ 14 - 0
expenses/templates/category_delete.html

@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+
+{# {% block page_title %}Liste des capteurs{% endblock %} #}
+
+{% block content %}
+{% load django_bootstrap5 %}
+
+<form method="post">
+    {% csrf_token %}
+    <p>Are you sure you want to delete "{{ object.name }}"?</p>
+    {% bootstrap_form form %}
+    {% bootstrap_button button_type="submit" content="Submit" %}
+</form>
+{% endblock content %}

+ 24 - 0
expenses/templates/category_detail.html

@@ -0,0 +1,24 @@
+{% extends "base.html" %}
+
+{% block page_title %}Category : {{ category.name }} {%endblock %}
+
+{% block content %}
+<dl>
+    <dt>Name</dt>
+    <dd>{{ category.name }}</dd>
+    <dt>Type</dt>
+    <dd>{{ category.type }}</dd>
+    <dt>Creation date</dt>
+    <dd>{{ category.created_at }}</dd>
+    <dt>Modification date</dt>
+    <dd>{{ category.modified_at }}</dd>
+</dl>
+
+{% if user.is_superuser %}
+<a href="{% url 'expenses:category_edit' pk=category.pk %}" class="btn btn-primary">Modifier</a>
+<a href="{% url 'expenses:category_delete' pk=category.pk %}" class="btn btn-danger">Supprimer</a>
+{% endif %}
+
+<a href="{% url 'expenses:categories_list' %}">Revenir à la liste</a>
+
+{% endblock content %}

+ 16 - 0
expenses/templates/expense_list.html

@@ -0,0 +1,16 @@
+{% extends "base.html" %}
+{% load django_bootstrap5 %}
+
+{% block page_title %}Expenses{% endblock %}
+
+
+{% block content %}
+
+<ul>
+    {% for expense in expenses %}
+    <li><a href="">{{ expense.content_object.name }}</a></li>
+    {% endfor %}
+</ul>
+<a class="btn btn-info" href="{% url 'expenses:expense_create' %}">Create category</a>
+
+{% endblock content %}

+ 14 - 0
expenses/templates/lone_expense_create.html

@@ -0,0 +1,14 @@
+{% extends "base.html" %}
+
+
+{% block content %}
+{% load django_bootstrap5 %}
+
+{% block page_title %}Create category{% endblock %}
+
+<form action="" method="post">
+    {% csrf_token %}
+    {% bootstrap_form form %}
+    {% bootstrap_button button_type="submit" content="Submit" %}
+</form>
+{% endblock content %}

+ 46 - 0
expenses/templates/raw_expenses_list.html

@@ -0,0 +1,46 @@
+{% extends "base.html" %}
+{% load django_bootstrap5 %}
+
+{% block page_title %}Expenses{% endblock %}
+
+
+{% block content %}
+
+<ul>
+    {% for expense in expenses %}
+    <li>{{ category }}</a></li>
+    {% endfor %}
+</ul>
+
+
+<div class="accordion" id="accordionPanelsStayOpenExample">
+    {% for expense in expenses %}
+    <div class="accordion-item">
+        <h2 class="accordion-header" id="panelsStayOpen-headingOne">
+            <button class="accordion-button" type="button" data-bs-toggle="collapse"
+                data-bs-target="#panelsStayOpen-collapseOne" aria-expanded="true"
+                aria-controls="panelsStayOpen-collapseOne">
+                {{ exepense.name }}
+            </button>
+        </h2>
+        <div id="panelsStayOpen-collapseOne" class="accordion-collapse collapse show"
+            aria-labelledby="panelsStayOpen-headingOne">
+            <div class="accordion-body">
+                <ul class="list-group">
+                    <li class="list-group-item">{{ expense.date }}</li>
+                    <li class="list-group-item">{{ expense.amount }}</li>
+                    {% if expense.category %}
+                    <li class="list-group-item">A third item</li>
+                    {% endif %}
+                    {% if expense.source %}
+                    <li class="list-group-item">A third item</li>
+                    {% endif %}
+                </ul>
+            </div>
+        </div>
+    </div>
+    {% endfor %}
+</div>
+
+
+{% endblock content %}

+ 23 - 2
expenses/urls.py

@@ -1,9 +1,30 @@
 from django.urls import path
 
-from expenses.views import category_list
+from expenses.views import (
+    category_list,
+    category_detail,
+    CategoryCreateView,
+    CategoryUpdateView,
+    CategoryDeleteView,
+    expenses_list,
+    lone_expense_create,
+)
 
 app_name = "expenses"
 
 urlpatterns = [
-    path("categories", category_list, name="list"),
+    path("categories", category_list, name="categories_list"),
+    path("categories/create", CategoryCreateView.as_view(), name="category_create"),
+    path("categories/detail/<int:pk>", category_detail, name="category_detail"),
+    path(
+        "categories/edit/<int:pk>", CategoryUpdateView.as_view(), name="category_edit"
+    ),
+    path(
+        "categories/delete/<int:pk>",
+        CategoryDeleteView.as_view(),
+        name="category_delete",
+    ),
+    # path("category/", CategoryView.as_view(), name="category"),
+    path("expenses", expenses_list, name="expense_list"),
+    path("expenses/lone/create", lone_expense_create, name="expense_create"),
 ]

+ 91 - 5
expenses/views.py

@@ -1,6 +1,22 @@
-from django.shortcuts import render
-from expenses.models import Category
-from expenses.forms import CategoryForm
+from django.shortcuts import render, redirect
+
+from django.views.generic import CreateView, UpdateView, DeleteView, ListView
+from django.urls import reverse_lazy
+from django.contrib.auth.mixins import (
+    LoginRequiredMixin,
+    UserPassesTestMixin,
+    PermissionRequiredMixin,
+)
+from django.contrib.messages.views import SuccessMessageMixin
+from django.views import View
+
+from expenses.models import Category, RawExpense, Expense, MultiplePaymentExepense
+from expenses.forms import (
+    CategoryForm,
+    ExpenseForm,
+    LoneExpenseForm,
+    MultiplePaymentExepenseForm,
+)
 
 
 def homepage(request):
@@ -12,8 +28,78 @@ def category_list(request):
     form = CategoryForm(request.GET or None)
     if form.is_valid():
         data = form.cleaned_data
-        # print(sensor.pk)
-        # return render(request, 'sensors/sensor_detail.html', {'sensor': sensor})
     return render(
         request, "categories_list.html", {"categories": categories, "form": form}
     )
+
+
+def category_detail(request, pk):
+    category = Category.objects.get(pk=pk)
+    return render(request, "category_detail.html", {"category": category})
+
+
+class CategoryCreateView(CreateView):
+    model = Category
+    fields = "__all__"
+    template_name = "category_create.html"
+    success_url = reverse_lazy("expenses:categories_list")
+    success_message = "Sensor removed successfully"
+
+
+class CategoryUpdateView(UpdateView):
+    model = Category
+    fields = "__all__"
+    template_name = "category_create.html"
+    success_url = reverse_lazy("expenses:categories_list")
+    success_message = "Category updated successfully"
+
+
+class CategoryDeleteView(UserPassesTestMixin, SuccessMessageMixin, DeleteView):
+    model = Category
+    fields = "__all__"
+    template_name = "category_delete.html"
+    success_url = reverse_lazy("expenses:categories_list")
+    success_message = "Category removed successfully"
+
+    def test_func(self):
+        return self.request.user.is_superuser
+
+
+def expenses_list(request):
+    expenses = Expense.objects.all()
+    form = ExpenseForm(request.GET or None)
+    if form.is_valid():
+        data = form.cleaned_data
+    return render(request, "expense_list.html", {"expenses": expenses, "form": form})
+
+
+def lone_expense_create(request):
+    form = LoneExpenseForm(request.POST or None)
+    if form.is_valid():
+        instance = form.save()
+        expense = Expense(
+            content_object=instance,
+        )
+        expense.save()
+        return redirect("expenses:expense_list")
+    return render(request, "lone_expense_create.html", {"form": form})
+
+
+def create_expense(request):
+    form = ExpenseForm(request.POST or None)
+    if form.is_valid():
+        instance = form.save()
+        expense = Expense(
+            content_object=instance,
+        )
+        expense.save()
+        #        return HttpResponseRedirect('/')
+        return redirect("expenses:expense_create")
+
+    return render(request, "create_expense.html", {"form": form})
+
+
+class RawExpenseListView(ListView):
+    model = RawExpense
+    fields = "__all__"
+    template_name = "raw_expenses_list.html"

+ 1 - 1
incomes/migrations/0001_initial.py

@@ -1,4 +1,4 @@
-# Generated by Django 4.1.3 on 2022-11-03 14:56
+# Generated by Django 4.1.3 on 2022-11-04 10:06
 
 from django.db import migrations, models
 

BIN
incomes/migrations/__pycache__/0001_initial.cpython-38.pyc