Commit 0bde8217 authored by impfundev's avatar impfundev

feat: book cover image

parent cb47d04a
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
**/__pycache__/** **/__pycache__/**
*.sqlite3 *.sqlite3
media media
uploads
# Backup files # # Backup files #
*.bak *.bak
......
...@@ -5,9 +5,15 @@ from books.models import Book ...@@ -5,9 +5,15 @@ from books.models import Book
class BookForm(forms.ModelForm): class BookForm(forms.ModelForm):
class Meta: class Meta:
model = Book model = Book
fields = ["title", "stock", "category", "description"] fields = ["cover_image", "title", "stock", "category", "description"]
widgets = { widgets = {
"cover_image": forms.FileInput(
attrs={
"placeholder": "Cover Image",
"class": "form-control",
}
),
"title": forms.TextInput( "title": forms.TextInput(
attrs={ attrs={
"placeholder": "Title", "placeholder": "Title",
......
# Generated by Django 5.0.6 on 2024-07-10 04:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('books', '0003_book_category'),
]
operations = [
migrations.AddField(
model_name='book',
name='cover_image',
field=models.ImageField(blank=True, null=True, upload_to='uploads'),
),
]
# Generated by Django 5.0.6 on 2024-07-10 05:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('books', '0004_book_cover_image'),
]
operations = [
migrations.AlterField(
model_name='book',
name='cover_image',
field=models.ImageField(blank=True, null=True, upload_to=''),
),
]
# Generated by Django 5.0.6 on 2024-07-10 05:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('books', '0005_alter_book_cover_image'),
]
operations = [
migrations.AlterField(
model_name='book',
name='cover_image',
field=models.ImageField(blank=True, null=True, upload_to='uploads'),
),
]
# Generated by Django 5.0.6 on 2024-07-10 05:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('books', '0006_alter_book_cover_image'),
]
operations = [
migrations.AlterField(
model_name='book',
name='cover_image',
field=models.ImageField(blank=True, null=True, upload_to='media/uploads'),
),
]
# Generated by Django 5.0.6 on 2024-07-10 05:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('books', '0007_alter_book_cover_image'),
]
operations = [
migrations.AlterField(
model_name='book',
name='cover_image',
field=models.ImageField(blank=True, null=True, upload_to='uploads'),
),
]
...@@ -9,6 +9,7 @@ class Book(models.Model): ...@@ -9,6 +9,7 @@ class Book(models.Model):
category = models.ForeignKey( category = models.ForeignKey(
to=Category, on_delete=models.CASCADE, blank=True, null=True to=Category, on_delete=models.CASCADE, blank=True, null=True
) )
cover_image = models.ImageField(upload_to="uploads", blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
......
<table class="table table-hover"> <table class="table table-hover">
<thead> <thead>
<tr class="table-primary"> <tr class="table-primary">
<th scope="col">Cover</th>
<th scope="col">Title</th> <th scope="col">Title</th>
<th scope="col">Category</th> <th scope="col">Category</th>
<th scope="col">Stock</th> <th scope="col">Stock</th>
...@@ -13,19 +14,34 @@ ...@@ -13,19 +14,34 @@
<tbody> <tbody>
{% if object_list %} {% for book in object_list %} {% if object_list %} {% for book in object_list %}
<tr> <tr>
<td>
{% if book.cover_image %}
<img
style="object-fit: contain"
width="100"
height="100"
src="{{ book.cover_image.url }}"
/>
{% endif %}
</td>
<td>{{ book.title }}</td> <td>{{ book.title }}</td>
<td>{{ book.category.name }}</td> <td>{{ book.category.name }}</td>
<td>{{ book.stock }}</td> <td>{{ book.stock }}</td>
<td>{{ book.description }}</td> <td>{{ book.description }}</td>
<td>{{ book.created_at }}</td> <td>{{ book.created_at }}</td>
<td>{{ book.updated_at }}</td> <td>{{ book.updated_at }}</td>
<td class="d-flex gap-2"> <td>
<a class="btn btn-success" href="/dashboard/books/{{ book.id }}/"> <div class="d-flex gap-2">
<i class="bi bi-pencil-square"></i> <a class="btn btn-success" href="/dashboard/books/{{ book.id }}/">
</a> <i class="bi bi-pencil-square"></i>
<a class="btn btn-danger" href="/dashboard/books/{{ book.id }}/delete/"> </a>
<i class="bi bi-trash3-fill"></i> <a
</a> class="btn btn-danger"
href="/dashboard/books/{{ book.id }}/delete/"
>
<i class="bi bi-trash3-fill"></i>
</a>
</div>
</td> </td>
</tr> </tr>
{% endfor %} {% else %} {% endfor %} {% else %}
...@@ -39,6 +55,7 @@ ...@@ -39,6 +55,7 @@
<td></td> <td></td>
<td></td> <td></td>
<td></td> <td></td>
<td></td>
</tr> </tr>
{% endif %} {% endif %}
</tbody> </tbody>
......
...@@ -10,8 +10,8 @@ For the full list of settings and their values, see ...@@ -10,8 +10,8 @@ For the full list of settings and their values, see
https://docs.djangoproject.com/en/5.0/ref/settings/ https://docs.djangoproject.com/en/5.0/ref/settings/
""" """
import os
from pathlib import Path from pathlib import Path
from os import getenv
from dotenv import load_dotenv from dotenv import load_dotenv
# Build paths inside the project like this: BASE_DIR / 'subdir'. # Build paths inside the project like this: BASE_DIR / 'subdir'.
...@@ -19,7 +19,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent ...@@ -19,7 +19,7 @@ BASE_DIR = Path(__file__).resolve().parent.parent
# .env # .env
load_dotenv() load_dotenv()
JWT_SECRET = getenv("JWT_SECRET", default="") JWT_SECRET = os.getenv("JWT_SECRET", default="")
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/
...@@ -104,6 +104,8 @@ TEMPLATES = [ ...@@ -104,6 +104,8 @@ TEMPLATES = [
"django.template.context_processors.request", "django.template.context_processors.request",
"django.contrib.auth.context_processors.auth", "django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages", "django.contrib.messages.context_processors.messages",
"django.template.context_processors.media",
# local
"authentications.context_processors.get_auth_session", "authentications.context_processors.get_auth_session",
], ],
}, },
...@@ -119,11 +121,11 @@ WSGI_APPLICATION = "config.wsgi.application" ...@@ -119,11 +121,11 @@ WSGI_APPLICATION = "config.wsgi.application"
DATABASES = { DATABASES = {
"default": { "default": {
"ENGINE": "django.db.backends.mysql", "ENGINE": "django.db.backends.mysql",
"NAME": getenv("DB_NAME"), "NAME": os.getenv("DB_NAME"),
"USER": getenv("DB_USER"), "USER": os.getenv("DB_USER"),
"PASSWORD": getenv("DB_PASSWORD"), "PASSWORD": os.getenv("DB_PASSWORD"),
"HOST": getenv("DB_HOST"), "HOST": os.getenv("DB_HOST"),
"PORT": getenv("DB_PORT"), "PORT": os.getenv("DB_PORT"),
} }
} }
...@@ -162,12 +164,18 @@ USE_TZ = True ...@@ -162,12 +164,18 @@ USE_TZ = True
# Static files (CSS, JavaScript, Images) # Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/5.0/howto/static-files/ # https://docs.djangoproject.com/en/5.0/howto/static-files/
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR / "staticfiles" STATIC_ROOT = BASE_DIR / "staticfiles"
STATIC_URL = "static/"
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR
STORAGES = { STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": { "staticfiles": {
"BACKEND": "whitenoise.storage.CompressedManifestStaticFilesStorage", "BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
}, },
} }
......
...@@ -15,10 +15,12 @@ Including another URLconf ...@@ -15,10 +15,12 @@ Including another URLconf
2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
""" """
from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.urls import path from django.urls import path
from django.conf.urls import include from django.conf.urls import include
from dashboards.views import HomePage from dashboards.views import HomePage
from django.conf.urls.static import static
urlpatterns = [ urlpatterns = [
path("", HomePage.as_view(), name="homepage"), path("", HomePage.as_view(), name="homepage"),
...@@ -35,3 +37,6 @@ urlpatterns = [ ...@@ -35,3 +37,6 @@ urlpatterns = [
), ),
path("api-auth/", include("rest_framework.urls")), path("api-auth/", include("rest_framework.urls")),
] ]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div style="max-width: 80vw" class="w-100 p-4"> <div style="max-width: 80vw" class="w-100 p-4">
<div class="d-flex flex-column gap-2 mb-4"> <div class="d-flex flex-column gap-2 mb-4">
<h1 class="h3">Add Data</h1> <h1 class="h3">Add Data</h1>
<form action="" method="POST"> <form method="POST" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div class="d-flex flex-column gap-1">{{ form }}</div> <div class="d-flex flex-column gap-1">{{ form }}</div>
<div class="d-flex gap-2 my-3"> <div class="d-flex gap-2 my-3">
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div style="max-width: 80vw" class="w-100 p-4"> <div style="max-width: 80vw" class="w-100 p-4">
<div class="d-flex flex-column gap-2 mb-4"> <div class="d-flex flex-column gap-2 mb-4">
<h1 class="h3">Are you sure want to delete this data <h1 class="h3">Are you sure want to delete this data
<form action="" method="POST"> <form method="POST" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div class="d-flex flex-column gap-1">Once data is deleted, it cannot be restored.</div> <div class="d-flex flex-column gap-1">Once data is deleted, it cannot be restored.</div>
<div class="d-flex gap-2 my-3"> <div class="d-flex gap-2 my-3">
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
<div style="max-width: 80vw" class="w-100 p-4"> <div style="max-width: 80vw" class="w-100 p-4">
<div class="d-flex flex-column gap-2 mb-4"> <div class="d-flex flex-column gap-2 mb-4">
<h1 class="h3">Update Data</h1> <h1 class="h3">Update Data</h1>
<form action="" method="POST"> <form method="POST" enctype="multipart/form-data">
{% csrf_token %} {% csrf_token %}
<div class="d-flex flex-column gap-1">{{ form }}</div> <div class="d-flex flex-column gap-1">{{ form }}</div>
<div class="d-flex gap-2 my-3"> <div class="d-flex gap-2 my-3">
......
# Generated by Django 5.0.6 on 2024-07-10 02:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('members', '0007_alter_members_account_number'),
]
operations = [
migrations.AlterField(
model_name='members',
name='account_number',
field=models.CharField(default='190415015691257', editable=False, max_length=15),
),
]
# Generated by Django 5.0.6 on 2024-07-10 04:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('members', '0008_alter_members_account_number'),
]
operations = [
migrations.AlterField(
model_name='members',
name='account_number',
field=models.CharField(default='022569062908676', editable=False, max_length=15),
),
]
# Generated by Django 5.0.6 on 2024-07-10 05:11
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('members', '0009_alter_members_account_number'),
]
operations = [
migrations.AlterField(
model_name='members',
name='account_number',
field=models.CharField(default='955979555055185', editable=False, max_length=15),
),
]
# Generated by Django 5.0.6 on 2024-07-10 05:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('members', '0010_alter_members_account_number'),
]
operations = [
migrations.AlterField(
model_name='members',
name='account_number',
field=models.CharField(default='574296497943013', editable=False, max_length=15),
),
]
# Generated by Django 5.0.6 on 2024-07-10 05:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('members', '0011_alter_members_account_number'),
]
operations = [
migrations.AlterField(
model_name='members',
name='account_number',
field=models.CharField(default='232120498178845', editable=False, max_length=15),
),
]
# Generated by Django 5.0.6 on 2024-07-10 05:30
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('members', '0012_alter_members_account_number'),
]
operations = [
migrations.AlterField(
model_name='members',
name='account_number',
field=models.CharField(default='733604449314241', editable=False, max_length=15),
),
]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment