fix form templates

This commit is contained in:
sankalp committed 2025-02-16 13:47:57 +05:45
1 parent 830cd73e08
commit 07faec097e
6 files changed
+269 -310

No files matched your search

+92 -77
View File
@@ -1,11 +1,12 @@
from django.contrib import admin from django.contrib import admin
from django import forms from django import forms
from django.shortcuts import render, redirect from django.shortcuts import render, redirect
from django.urls import path from django.urls import path, reverse
from django.http import JsonResponse from django.http import JsonResponse
from django.contrib.admin import site from django.contrib.admin import site
from .models import News, NewsImage, Category, SubCategory, Advertisement, Organization from .models import News, NewsImage, Category, SubCategory, Advertisement, Organization
@admin.register(Organization) @admin.register(Organization)
class OrganizationAdmin(admin.ModelAdmin): class OrganizationAdmin(admin.ModelAdmin):
list_display = ("id", "name", "location", "email", "phone_no", "logo") list_display = ("id", "name", "location", "email", "phone_no", "logo")
@@ -30,87 +31,101 @@ class NewsImageInline(admin.TabularInline):
extra = 1 extra = 1
# class NewsAdminForm(forms.ModelForm): class NewsAdminForm(forms.ModelForm):
# class Meta: class Meta:
# model = News model = News
# fields = ['title', 'description', 'image', 'category', 'sub_category'] fields = ["title", "description", "image", "category", "sub_category"]
# def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
# super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
# self.fields['sub_category'].queryset = SubCategory.objects.none() self.fields["sub_category"].queryset = SubCategory.objects.none()
# # If we have an instance and category is set # If we have an instance and category is set
# if self.instance and self.instance.pk and self.instance.category: if self.instance and self.instance.pk and self.instance.category:
# self.fields['sub_category'].queryset = SubCategory.objects.filter( self.fields["sub_category"].queryset = SubCategory.objects.filter(
# category=self.instance.category category=self.instance.category
# ) )
# # If form data is passed and category is selected # If form data is passed and category is selected
# if args and args[0] and 'category' in args[0]: if args and args[0] and "category" in args[0]:
# try: try:
# category_id = int(args[0]['category']) category_id = int(args[0]["category"])
# self.fields['sub_category'].queryset = SubCategory.objects.filter( self.fields["sub_category"].queryset = SubCategory.objects.filter(
# category_id=category_id category_id=category_id
# ) )
# except (ValueError, TypeError): except (ValueError, TypeError):
# pass pass
@admin.register(News) @admin.register(News)
class NewsAdmin(admin.ModelAdmin): class NewsAdmin(admin.ModelAdmin):
# form = NewsAdminForm form = NewsAdminForm
inlines = [NewsImageInline] inlines = [NewsImageInline]
list_display = ['title', 'category', 'sub_category', 'created_at', 'updated_at'] list_display = ["title", "category", "sub_category", "created_at", "updated_at"]
# def get_urls(self): def get_urls(self):
# urls = super().get_urls() urls = super().get_urls()
# custom_urls = [ custom_urls = [
# path('add/', self.admin_site.admin_view(self.add_news_view), name='news_news_add'), path(
# path('get_subcategories/', self.admin_site.admin_view(self.get_subcategories), name='get_subcategories'), "add/",
# ] self.admin_site.admin_view(self.add_news_view),
# return custom_urls + urls name="news_news_add",
# def get_subcategories(self, request): ),
# category_id = request.GET.get('category_id') path(
# if category_id: "get_subcategories/",
# subcategories = SubCategory.objects.filter(category_id=category_id).values('id', 'name') self.admin_site.admin_view(self.get_subcategories),
# return JsonResponse(list(subcategories), safe=False) name="get_subcategories",
# return JsonResponse([], safe=False) ),
# def add_news_view(self, request): ]
# if request.method == 'POST' and '_select_category' in request.POST: return custom_urls + urls
# # Just handling category selection
# form = self.form(request.POST) def get_subcategories(self, request):
# if 'category' in request.POST and request.POST['category']: category_id = request.GET.get("category_id")
# category_id = request.POST['category'] if category_id:
# form.fields['sub_category'].queryset = SubCategory.objects.filter( subcategories = SubCategory.objects.filter(category_id=category_id).values(
# category_id=category_id "id", "name"
# ) )
# elif request.method == 'POST': return JsonResponse(list(subcategories), safe=False)
# # Final form submission return JsonResponse([], safe=False)
# form = self.form(request.POST, request.FILES)
# if form.is_valid(): def add_news_view(self, request):
# news = form.save() if request.method == "POST" and "_select_category" in request.POST:
# self.message_user(request, "News created successfully.") # Just handling category selection
# return redirect('admin:news_news_change', object_id=news.pk) form = self.form(request.POST)
# else: if "category" in request.POST and request.POST["category"]:
# form = self.form() category_id = request.POST["category"]
form.fields["sub_category"].queryset = SubCategory.objects.filter(
# # Get all categories for the dropdown category_id=category_id
# categories = Category.objects.all() )
elif request.method == "POST":
# # Prepare context for the template # Final form submission
# context = { form = self.form(request.POST, request.FILES)
# 'form': form, if form.is_valid():
# **site.each_context(request), news = form.save()
# 'categories': categories, self.message_user(request, "News created successfully.")
# 'opts': self.model._meta, return redirect(reverse("admin:app_news_changelist"))
# 'has_view_permission': self.has_view_permission(request),
# 'has_add_permission': self.has_add_permission(request), else:
# 'has_change_permission': self.has_change_permission(request), form = self.form()
# 'has_delete_permission': self.has_delete_permission(request),
# 'app_label': self.model._meta.app_label, # Get all categories for the dropdown
# } categories = Category.objects.all()
# return render(request, 'admin/app/news/add_form.html', context) # Prepare context for the template
context = {
"form": form,
**site.each_context(request),
"categories": categories,
"opts": self.model._meta,
"has_view_permission": self.has_view_permission(request),
"has_add_permission": self.has_add_permission(request),
"has_change_permission": self.has_change_permission(request),
"has_delete_permission": self.has_delete_permission(request),
"app_label": self.model._meta.app_label,
}
return render(request, "admin/app/news/add_form.html", context)
@admin.register(Advertisement) @admin.register(Advertisement)
class AdvertisementAdmin(admin.ModelAdmin): class AdvertisementAdmin(admin.ModelAdmin):
+6 -4
View File
@@ -1,6 +1,7 @@
from django.db import models from django.db import models
from django_ckeditor_5.fields import CKEditor5Field from django_ckeditor_5.fields import CKEditor5Field
class Organization(models.Model): class Organization(models.Model):
name = models.CharField(max_length=255) name = models.CharField(max_length=255)
location = models.CharField(max_length=255) location = models.CharField(max_length=255)
@@ -45,10 +46,12 @@ class SubCategory(models.Model):
class News(models.Model): class News(models.Model):
id = models.BigAutoField(primary_key=True) id = models.BigAutoField(primary_key=True)
title = models.CharField(max_length=255) title = models.CharField(max_length=255)
description = models.TextField(null=True, blank=True, max_length=30000) description = models.TextField(null=True, blank=True, max_length=30000)
image = models.ImageField(upload_to="news") image = models.ImageField(upload_to="news")
category = models.ForeignKey(Category, on_delete=models.CASCADE) category = models.ForeignKey(Category, on_delete=models.CASCADE)
sub_category = models.ForeignKey(SubCategory, on_delete=models.CASCADE, null=True, blank=True) sub_category = models.ForeignKey(
SubCategory, on_delete=models.CASCADE, null=True, blank=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)
@@ -62,7 +65,7 @@ class News(models.Model):
class NewsImage(models.Model): class NewsImage(models.Model):
news = models.ForeignKey(News, on_delete=models.CASCADE, related_name="images") news = models.ForeignKey(News, on_delete=models.CASCADE, related_name="images")
image = models.ImageField(upload_to="news/images") image = models.ImageField(upload_to="news/images", blank=True)
def __str__(self): def __str__(self):
return f"Image for {self.news.title}" return f"Image for {self.news.title}"
@@ -77,7 +80,6 @@ class Advertisement(models.Model):
def __str__(self): def __str__(self):
return str(self.id) return str(self.id)
class Meta: class Meta:
ordering = ["id"] ordering = ["id"]
verbose_name_plural = "ads" verbose_name_plural = "ads"
+121 -196
View File
@@ -1,212 +1,137 @@
{% extends "admin/change_form.html" %} {% extends 'admin/change_form.html' %}
{% load i18n admin_urls static admin_modify %} {% load static %}
{% load widget_tweaks %}
{% block extrahead %} {% block extrastyle %}
{{ block.super }} {{ block.super }}
<style> <style>
/* Main container styling */ .nav-tabs .nav-link { color: #444; }
.jazzmin-form-wrapper { .nav-tabs .nav-link.active {
padding: 20px; color: #007bff;
width: 100%; border-bottom: 2px solid #007bff;
background-color: white;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
/* Form layout */
.form-grid {
display: grid;
grid-template-columns: 180px 1fr;
gap: 15px 20px;
align-items: start;
}
/* Field styling */
.field-wrapper {
margin-bottom: 20px;
}
.field-label {
font-weight: 600;
color: #333;
margin-top: 8px;
text-align: right;
}
.field-input {
width: 100%;
}
/* Input styling */
input[type="text"],
textarea,
select {
width: 100%;
max-width: 500px;
padding: 8px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #fff;
}
textarea {
min-height: 150px;
}
/* Error styling */
.field-error {
color: #e74c3c;
font-size: 0.9em;
margin-top: 4px;
}
/* Button styling */
.submit-row {
margin-top: 30px;
display: flex;
justify-content: center;
}
/* Responsive adjustments */
@media (max-width: 768px) {
.form-grid {
grid-template-columns: 1fr;
} }
.card {
.field-label { box-shadow: 0 0 1px rgba(0,0,0,.125), 0 1px 3px rgba(0,0,0,.2);
text-align: left; margin-bottom: 1rem;
margin-bottom: 5px; border-radius: .25rem;
} }
} .card-header {
</style> background-color: #f8f9fa;
border-bottom: 1px solid rgba(0,0,0,.125);
padding: .75rem 1.25rem;
}
.card-body { padding: 1.25rem; }
.card-footer {
background-color: #f8f9fa;
border-top: 1px solid rgba(0,0,0,.125);
padding: .75rem 1.25rem;
}
.form-control:focus {
border-color: #80bdff;
box-shadow: 0 0 0 0.2rem rgba(0,123,255,.25);
}
</style>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="jazzmin-form-wrapper"> <div class="container-fluid">
<form method="post" enctype="multipart/form-data" id="news_form" novalidate> <div class="card">
{% csrf_token %} <div class="card-header">
<ul class="nav nav-tabs card-header-tabs">
{% if errors %} <li class="nav-item">
<div class="errornote" style="margin-bottom: 20px; padding: 10px; background-color: #fef2f2; border-left: 4px solid #e74c3c; color: #e74c3c;"> <a class="nav-link active" data-toggle="tab" href="#general">General</a>
{% blocktranslate count counter=errors|length %}Please correct the error below.{% plural %}Please correct the errors below.{% endblocktranslate %} </li>
{{ form.non_field_errors }} <li class="nav-item">
</div> <a class="nav-link" data-toggle="tab" href="#news-images">News Images</a>
{% endif %} </li>
</ul>
<div class="form-grid"> </div>
<!-- Title Field --> <form method="post" enctype="multipart/form-data">
<div class="field-label">{{ form.title.label }}:</div> {% csrf_token %}
<div class="field-input"> <div class="card-body">
{% if form.title.errors %} <div class="tab-content">
<div class="field-error">{{ form.title.errors }}</div> <div class="tab-pane fade show active" id="general">
{% endif %} <div class="row">
{{ form.title }} <div class="col-md-6">
</div> <div class="form-group">
<label for="{{ form.title.id_for_label }}">{{ form.title.label }}</label>
<!-- Description Field --> {{ form.title|add_class:"form-control" }}
<div class="field-label">{{ form.description.label }}:</div> {% if form.title.errors %}<div class="invalid-feedback d-block">{{ form.title.errors }}</div>{% endif %}
<div class="field-input"> </div>
{% if form.description.errors %} </div>
<div class="field-error">{{ form.description.errors }}</div> <div class="col-md-6">
{% endif %} <div class="form-group">
{{ form.description }} <label for="{{ form.category.id_for_label }}">{{ form.category.label }}</label>
</div> <select name="category"
id="{{ form.category.id_for_label }}"
<!-- Image Field --> class="form-control"
<div class="field-label">{{ form.image.label }}:</div> onchange="updateSubcategories(this.value)">
<div class="field-input"> <option value="">---------</option>
{% if form.image.errors %} {% for category in categories %}
<div class="field-error">{{ form.image.errors }}</div> <option value="{{ category.id }}"
{% endif %} {% if form.category.value|stringformat:"s" == category.id|stringformat:"s" %}selected{% endif %}>
{{ form.image }} {{ category.name }}
</div> </option>
{% endfor %}
<!-- Category Field --> </select>
<div class="field-label">{{ form.category.label }}:</div> {% if form.category.errors %}<div class="invalid-feedback d-block">{{ form.category.errors }}</div>{% endif %}
<div class="field-input"> </div>
{% if form.category.errors %} </div>
<div class="field-error">{{ form.category.errors }}</div> </div>
{% endif %} <div class="form-group" id="subcategory_wrapper" style="display: none;">
<select name="category" id="{{ form.category.id_for_label }}" onchange="updateSubcategories(this.value)"> <label for="{{ form.sub_category.id_for_label }}">{{ form.sub_category.label }}</label>
<option value="">---------</option> <div id="subcategory_container">{{ form.sub_category }}</div>
{% for category in categories %} </div>
<option value="{{ category.id }}" {% if form.category.value|stringformat:"s" == category.id|stringformat:"s" %}selected{% endif %}> <div class="form-group">
{{ category.name }} <label for="{{ form.description.id_for_label }}">{{ form.description.label }}</label>
</option> {{ form.description|add_class:"form-control" }}
{% endfor %} </div>
</select> <div class="form-group">
</div> <label for="{{ form.image.id_for_label }}">{{ form.image.label }}</label>
{{ form.image|add_class:"form-control-file" }}
<!-- Subcategory Field --> </div>
<div class="field-label" id="subcategory_label" {% if not form.sub_category.queryset.exists %}style="display: none;"{% endif %}> </div>
{{ form.sub_category.label }}: <div class="tab-pane fade" id="news-images">
</div> <div class="form-group">
<div class="field-input" id="subcategory_row" {% if not form.sub_category.queryset.exists %}style="display: none;"{% endif %}> <label>Upload Additional News Images</label>
{% if form.sub_category.errors %} <input type="file" name="news_images" multiple class="form-control">
<div class="field-error">{{ form.sub_category.errors }}</div> </div>
{% endif %} </div>
<div id="subcategory_container"> </div>
{{ form.sub_category }} </div>
<div class="card-footer text-right">
<button type="submit" class="btn btn-success">Save</button>
<button type="submit" name="_addanother" class="btn btn-info">Save and add another</button>
<button type="submit" name="_continue" class="btn btn-primary">Save and continue editing</button>
</div>
</form>
</div> </div>
</div>
</div> </div>
{% for inline_admin_formset in inline_admin_formsets %} <script>
{% include inline_admin_formset.opts.template %}
{% endfor %}
<div class="submit-row">
<input type="submit" name="_save" value="{% translate 'Save' %}" class="default" style="padding: 8px 16px; background-color: #2980b9; color: white; border: none; border-radius: 4px; cursor: pointer;" />
</div>
</form>
</div>
<script>
function updateSubcategories(categoryId) { function updateSubcategories(categoryId) {
const labelElement = document.getElementById('subcategory_label'); const wrapper = document.getElementById('subcategory_wrapper');
const rowElement = document.getElementById('subcategory_row'); const container = document.getElementById('subcategory_container');
if (!categoryId) { if (!categoryId) {
labelElement.style.display = 'none'; wrapper.style.display = 'none';
rowElement.style.display = 'none';
return; return;
} }
// Show elements wrapper.style.display = 'block';
labelElement.style.display = 'block'; container.innerHTML = '<div class="text-info">Loading subcategories...</div>';
rowElement.style.display = 'block';
// Show loading indicator
const container = document.getElementById('subcategory_container');
container.innerHTML = '<div style="color: #2980b9;">Loading subcategories...</div>';
// Fetch subcategories with AJAX
fetch(`{% url 'admin:get_subcategories' %}?category_id=${categoryId}`) fetch(`{% url 'admin:get_subcategories' %}?category_id=${categoryId}`)
.then(response => response.json()) .then(response => response.json())
.then(data => { .then(data => {
if (data.length === 0) { let selectHtml = `<select name="sub_category" class="form-control">`;
container.innerHTML = '<select id="{{ form.sub_category.id_for_label }}" name="sub_category"><option value="">No subcategories available</option></select>'; selectHtml += `<option value="">Select Subcategory</option>`;
} else { data.forEach(item => {
let selectHtml = `<select id="{{ form.sub_category.id_for_label }}" name="sub_category">`; selectHtml += `<option value="${item.id}">${item.name}</option>`;
data.forEach(item => { });
selectHtml += `<option value="${item.id}">${item.name}</option>`; selectHtml += '</select>';
}); container.innerHTML = selectHtml;
selectHtml += '</select>';
container.innerHTML = selectHtml;
}
}) })
.catch(error => { .catch(() => {
container.innerHTML = '<div style="color: #e74c3c;">Error loading subcategories</div>'; container.innerHTML = '<div class="text-danger">Error loading subcategories</div>';
console.error('Error:', error);
}); });
} }
</script>
// Initialize subcategories if category is already selected {% endblock %}
document.addEventListener('DOMContentLoaded', function() {
const categorySelect = document.getElementById('{{ form.category.id_for_label }}');
if (categorySelect.value) {
updateSubcategories(categorySelect.value);
}
});
</script>
{% endblock %}
+17 -7
View File
@@ -2,11 +2,21 @@
from django.urls import path from django.urls import path
from . import views from . import views
urlpatterns = [ urlpatterns = [
path('news/', views.NewsListView.as_view(), name='news-list'), path("news/", views.NewsListView.as_view(), name="news-list"),
path('news/<int:id>/', views.NewsDetailView.as_view(), name='news-detail'), path("news/<int:id>/", views.NewsDetailView.as_view(), name="news-detail"),
path('categories/', views.CategoryListView.as_view(), name='category-list'), path("categories/", views.CategoryListView.as_view(), name="category-list"),
path('subcategories/', views.SubCategoryListView.as_view(), name='subcategory-list'), path(
path('advertisements/', views.AdvertisementListView.as_view(), name='advertisement-list'), "subcategories/", views.SubCategoryListView.as_view(), name="subcategory-list"
path('organization/', views.OrganizationListView.as_view(), name='organization-list'), ),
] path(
"advertisements/",
views.AdvertisementListView.as_view(),
name="advertisement-list",
),
path(
"organization/", views.OrganizationListView.as_view(), name="organization-list"
),
]
+20 -14
View File
@@ -4,49 +4,55 @@ from django.shortcuts import get_object_or_404
from rest_framework.exceptions import ValidationError from rest_framework.exceptions import ValidationError
from django.db.models import Q from django.db.models import Q
from .serializers import * from .serializers import *
from .views import News, Category, SubCategory, Advertisement, Organization from .models import News, Category, SubCategory, Advertisement, Organization
class NewsListView(generics.ListAPIView): class NewsListView(generics.ListAPIView):
serializer_class = NewsSerializer serializer_class = NewsSerializer
def get_queryset(self): def get_queryset(self):
queryset = News.objects.all().order_by('-created_at') queryset = News.objects.all().order_by("-created_at")
category = self.request.query_params.get('category', None) category = self.request.query_params.get("category", None)
subcategory = self.request.query_params.get('subcategory', None) subcategory = self.request.query_params.get("subcategory", None)
if category: if category:
queryset = queryset.filter( queryset = queryset.filter(
Q(category__name__iexact=category) | Q(category__name__iexact=category) | Q(category__id=category)
Q(category__id=category)
) )
if subcategory: if subcategory:
queryset = queryset.filter(sub_category_id=subcategory) queryset = queryset.filter(sub_category_id=subcategory)
return queryset return queryset
class NewsDetailView(generics.RetrieveAPIView): class NewsDetailView(generics.RetrieveAPIView):
queryset = News.objects.all() queryset = News.objects.all()
serializer_class = NewsSerializer serializer_class = NewsSerializer
lookup_field = 'id' lookup_field = "id"
class CategoryListView(generics.ListAPIView): class CategoryListView(generics.ListAPIView):
queryset = Category.objects.prefetch_related('subcategories') queryset = Category.objects.prefetch_related("subcategories")
serializer_class = CategoryListSerializer serializer_class = CategoryListSerializer
class SubCategoryListView(generics.ListAPIView): class SubCategoryListView(generics.ListAPIView):
serializer_class = SubCategorySerializer serializer_class = SubCategorySerializer
def get_queryset(self): def get_queryset(self):
queryset = SubCategory.objects.all() queryset = SubCategory.objects.all()
category_id = self.request.query_params.get('category', None) category_id = self.request.query_params.get("category", None)
if category_id: if category_id:
queryset = queryset.filter(category_id=category_id) queryset = queryset.filter(category_id=category_id)
return queryset return queryset
class AdvertisementListView(generics.ListAPIView): class AdvertisementListView(generics.ListAPIView):
queryset = Advertisement.objects.filter(is_active=True) queryset = Advertisement.objects.filter(is_active=True)
serializer_class = AdvertisementSerializer serializer_class = AdvertisementSerializer
class OrganizationListView(generics.ListAPIView): class OrganizationListView(generics.ListAPIView):
queryset = Organization.objects.all() queryset = Organization.objects.all()
serializer_class = OrganizationSerializer serializer_class = OrganizationSerializer
+13 -12
View File
@@ -40,6 +40,7 @@ INSTALLED_APPS = [
"rest_framework", "rest_framework",
"corsheaders", "corsheaders",
"django_ckeditor_5", "django_ckeditor_5",
"widget_tweaks",
] ]
@@ -79,24 +80,24 @@ CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_ALL_ORIGINS = True CORS_ALLOW_ALL_ORIGINS = True
CORS_ALLOW_CREDENTIALS = True CORS_ALLOW_CREDENTIALS = True
# Also ensure you have this setting # Also ensure you have this setting
SESSION_COOKIE_SAMESITE = 'Lax' # or 'None' if needed SESSION_COOKIE_SAMESITE = "Lax" # or 'None' if needed
CSRF_COOKIE_SAMESITE = 'Lax' # or 'None' if needed CSRF_COOKIE_SAMESITE = "Lax" # or 'None' if needed
CSRF_TRUSTED_ORIGINS = [ CSRF_TRUSTED_ORIGINS = [
'http://192.168.1.93:5173', "http://192.168.1.93:5173",
'http://192.168.18.4:5173', "http://192.168.18.4:5173",
'http://192.168.1.68:5173', "http://192.168.1.68:5173",
'http://192.168.1.87:5173', "http://192.168.1.87:5173",
'http://localhost:5173' "http://localhost:5173",
] ]
if DEBUG: if DEBUG:
CSRF_COOKIE_SECURE = False CSRF_COOKIE_SECURE = False
SESSION_COOKIE_SECURE = False SESSION_COOKIE_SECURE = False
CORS_ALLOWED_ORIGINS = [ CORS_ALLOWED_ORIGINS = [
'http://192.168.1.93:5173', "http://192.168.1.93:5173",
'http://192.168.18.4:5173', "http://192.168.18.4:5173",
'http://192.168.1.68:5173', "http://192.168.1.68:5173",
'http://192.168.1.87:5173', "http://192.168.1.87:5173",
'http://localhost:5173' "http://localhost:5173",
] ]
WSGI_APPLICATION = "newsportal.wsgi.application" WSGI_APPLICATION = "newsportal.wsgi.application"