fix form templates
This commit is contained in:
6 files changed
+269
-310
No files matched your search
+92
-77
@@ -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
@@ -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"
|
||||||
@@ -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
@@ -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
@@ -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
@@ -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"
|
||||||
|
|||||||
Reference in new issue
Block a user