Initial commit: Django backend with Campaign, ContactUs, and Composition APIs
This commit is contained in:
0
api/__init__.py
Normal file
0
api/__init__.py
Normal file
72
api/admin.py
Normal file
72
api/admin.py
Normal file
@@ -0,0 +1,72 @@
|
||||
from django.contrib import admin
|
||||
from .models import ContactUs, Composition, Campaign
|
||||
|
||||
|
||||
@admin.register(ContactUs)
|
||||
class ContactUsAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'email_or_phone', 'category', 'created_at']
|
||||
list_filter = ['category', 'created_at']
|
||||
search_fields = ['name', 'email_or_phone', 'description']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
date_hierarchy = 'created_at'
|
||||
|
||||
fieldsets = (
|
||||
('اطلاعات تماس', {
|
||||
'fields': ('name', 'email_or_phone', 'category')
|
||||
}),
|
||||
('پیام', {
|
||||
'fields': ('description',)
|
||||
}),
|
||||
('اطلاعات زمانی', {
|
||||
'fields': ('created_at', 'updated_at'),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@admin.register(Composition)
|
||||
class CompositionAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'created_at']
|
||||
search_fields = ['name', 'description']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
date_hierarchy = 'created_at'
|
||||
|
||||
fieldsets = (
|
||||
('اطلاعات ترکیب', {
|
||||
'fields': ('name', 'description', 'image')
|
||||
}),
|
||||
('اطلاعات زمانی', {
|
||||
'fields': ('created_at', 'updated_at'),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
)
|
||||
|
||||
|
||||
@admin.register(Campaign)
|
||||
class CampaignAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'start_time', 'end_time', 'is_active', 'created_at']
|
||||
list_filter = ['start_time', 'end_time', 'created_at']
|
||||
search_fields = ['name', 'description']
|
||||
readonly_fields = ['created_at', 'updated_at', 'is_active']
|
||||
date_hierarchy = 'start_time'
|
||||
|
||||
fieldsets = (
|
||||
('اطلاعات کمپین', {
|
||||
'fields': ('name', 'description', 'image')
|
||||
}),
|
||||
('زمانبندی', {
|
||||
'fields': ('start_time', 'end_time')
|
||||
}),
|
||||
('وضعیت', {
|
||||
'fields': ('is_active',)
|
||||
}),
|
||||
('اطلاعات زمانی', {
|
||||
'fields': ('created_at', 'updated_at'),
|
||||
'classes': ('collapse',)
|
||||
}),
|
||||
)
|
||||
|
||||
def is_active(self, obj):
|
||||
return obj.is_active()
|
||||
is_active.boolean = True
|
||||
is_active.short_description = 'فعال'
|
||||
6
api/apps.py
Normal file
6
api/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ApiConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'api'
|
||||
0
api/management/__init__.py
Normal file
0
api/management/__init__.py
Normal file
0
api/management/commands/__init__.py
Normal file
0
api/management/commands/__init__.py
Normal file
125
api/management/commands/seed_data.py
Normal file
125
api/management/commands/seed_data.py
Normal file
@@ -0,0 +1,125 @@
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.utils import timezone
|
||||
from datetime import timedelta
|
||||
from api.models import ContactUs, Composition, Campaign
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
help = 'Seed the database with Persian sample data'
|
||||
|
||||
def handle(self, *args, **options):
|
||||
self.stdout.write(self.style.SUCCESS('Starting to seed database with Persian data...'))
|
||||
|
||||
# Seed ContactUs data
|
||||
self.stdout.write('Creating ContactUs records...')
|
||||
contact_data = [
|
||||
{
|
||||
'name': 'احمد محمدی',
|
||||
'email_or_phone': 'ahmad.mohammadi@example.com',
|
||||
'description': 'سلام، من علاقهمند به همکاری با شرکت شما هستم. لطفاً اطلاعات بیشتری در مورد فرصتهای شغلی ارسال کنید.',
|
||||
'category': 'همکاری'
|
||||
},
|
||||
{
|
||||
'name': 'فاطمه رضایی',
|
||||
'email_or_phone': '09123456789',
|
||||
'description': 'با سلام، میخواستم در مورد محصولات شما اطلاعات بیشتری دریافت کنم. آیا امکان بازدید از نمایشگاه وجود دارد؟',
|
||||
'category': 'فروش'
|
||||
},
|
||||
{
|
||||
'name': 'علی کریمی',
|
||||
'email_or_phone': 'ali.karimi@example.com',
|
||||
'description': 'سلام، من مشکلی در استفاده از سرویس شما دارم. لطفاً راهنمایی کنید.',
|
||||
'category': 'پشتیبانی'
|
||||
},
|
||||
{
|
||||
'name': 'زهرا احمدی',
|
||||
'email_or_phone': '09187654321',
|
||||
'description': 'با احترام، میخواستم در مورد پروژههای سرمایهگذاری شما مشاوره دریافت کنم. لطفاً با من تماس بگیرید.',
|
||||
'category': 'درخواست مشاور'
|
||||
},
|
||||
{
|
||||
'name': 'محمد حسینی',
|
||||
'email_or_phone': 'mohammad.hosseini@example.com',
|
||||
'description': 'سلام، سوالی در مورد خدمات شما دارم که در دستهبندیهای موجود نیست. لطفاً راهنمایی کنید.',
|
||||
'category': 'سایر'
|
||||
}
|
||||
]
|
||||
|
||||
for i, data in enumerate(contact_data, 1):
|
||||
ContactUs.objects.create(**data)
|
||||
self.stdout.write(f' Created contact {i}/5')
|
||||
|
||||
# Seed Composition data
|
||||
self.stdout.write('Creating Composition records...')
|
||||
composition_data = [
|
||||
{
|
||||
'name': 'ترکیب استراتژیک زونکو',
|
||||
'description': 'این ترکیب شامل بخشهای مختلف شرکت زونکو میباشد که شامل مدیریت، توسعه، بازاریابی و فروش است. این ترکیب به گونهای طراحی شده که بتواند حداکثر کارایی را در تمامی بخشها ایجاد کند.'
|
||||
},
|
||||
{
|
||||
'name': 'ترکیب تکنولوژی و نوآوری',
|
||||
'description': 'ترکیبی از تکنولوژیهای پیشرفته و نوآوریهای روز دنیا که در شرکت زونکو به کار گرفته میشود. این ترکیب شامل هوش مصنوعی، بلاک چین و سایر تکنولوژیهای نوین است.'
|
||||
},
|
||||
{
|
||||
'name': 'ترکیب منابع انسانی',
|
||||
'description': 'ترکیب بهینه از نیروی انسانی متخصص و با تجربه که در بخشهای مختلف شرکت فعالیت میکنند. این ترکیب شامل مدیران ارشد، متخصصان فنی و کارشناسان بازاریابی است.'
|
||||
},
|
||||
{
|
||||
'name': 'ترکیب مالی و سرمایهگذاری',
|
||||
'description': 'ترکیب استراتژیک از منابع مالی و فرصتهای سرمایهگذاری که شرکت زونکو در اختیار دارد. این ترکیب شامل سرمایهگذاریهای داخلی و خارجی و مدیریت ریسک است.'
|
||||
},
|
||||
{
|
||||
'name': 'ترکیب بازاریابی و فروش',
|
||||
'description': 'ترکیب موثر از استراتژیهای بازاریابی و فروش که برای دستیابی به اهداف تجاری شرکت طراحی شده است. این ترکیب شامل بازاریابی دیجیتال، روابط عمومی و فروش مستقیم است.'
|
||||
}
|
||||
]
|
||||
|
||||
for i, data in enumerate(composition_data, 1):
|
||||
Composition.objects.create(**data)
|
||||
self.stdout.write(f' Created composition {i}/5')
|
||||
|
||||
# Seed Campaign data
|
||||
self.stdout.write('Creating Campaign records...')
|
||||
now = timezone.now()
|
||||
|
||||
campaign_data = [
|
||||
{
|
||||
'name': 'کمپین تابستانه زونکو ۱۴۰۳',
|
||||
'description': 'کمپین ویژه تابستان ۱۴۰۳ با تخفیفهای ویژه و جوایز نقدی. این کمپین شامل تمامی محصولات و خدمات شرکت میباشد و فرصت مناسبی برای مشتریان است.',
|
||||
'start_time': now - timedelta(days=30),
|
||||
'end_time': now + timedelta(days=30)
|
||||
},
|
||||
{
|
||||
'name': 'کمپین همکاری استراتژیک',
|
||||
'description': 'کمپین جذب شرکای تجاری جدید برای همکاری در پروژههای بزرگ. این کمپین فرصت مناسبی برای شرکتهایی است که میخواهند با زونکو همکاری کنند.',
|
||||
'start_time': now - timedelta(days=15),
|
||||
'end_time': now + timedelta(days=45)
|
||||
},
|
||||
{
|
||||
'name': 'کمپین نوآوری و تکنولوژی',
|
||||
'description': 'کمپین معرفی آخرین نوآوریها و تکنولوژیهای شرکت زونکو. این کمپین شامل نمایشگاهها، سمینارها و رویدادهای تخصصی است.',
|
||||
'start_time': now + timedelta(days=10),
|
||||
'end_time': now + timedelta(days=70)
|
||||
},
|
||||
{
|
||||
'name': 'کمپین سرمایهگذاری زونکو',
|
||||
'description': 'کمپین جذب سرمایهگذاران برای پروژههای جدید شرکت. این کمپین شامل ارائه اطلاعات کامل در مورد فرصتهای سرمایهگذاری و بازدهی مورد انتظار است.',
|
||||
'start_time': now - timedelta(days=60),
|
||||
'end_time': now - timedelta(days=10)
|
||||
},
|
||||
{
|
||||
'name': 'کمپین آموزش و توسعه',
|
||||
'description': 'کمپین ارائه دورههای آموزشی و کارگاههای تخصصی برای توسعه مهارتهای حرفهای. این کمپین شامل دورههای آنلاین و حضوری است.',
|
||||
'start_time': now + timedelta(days=5),
|
||||
'end_time': now + timedelta(days=65)
|
||||
}
|
||||
]
|
||||
|
||||
for i, data in enumerate(campaign_data, 1):
|
||||
Campaign.objects.create(**data)
|
||||
self.stdout.write(f' Created campaign {i}/5')
|
||||
|
||||
self.stdout.write(self.style.SUCCESS('\nSuccessfully seeded database with Persian data!'))
|
||||
self.stdout.write(f' - ContactUs: {ContactUs.objects.count()} records')
|
||||
self.stdout.write(f' - Composition: {Composition.objects.count()} records')
|
||||
self.stdout.write(f' - Campaign: {Campaign.objects.count()} records')
|
||||
68
api/migrations/0001_initial.py
Normal file
68
api/migrations/0001_initial.py
Normal file
@@ -0,0 +1,68 @@
|
||||
# Generated by Django 4.2.7 on 2025-12-17 12:13
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ContactUs',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, verbose_name='نام و نام خانوادگی')),
|
||||
('email_or_phone', models.CharField(help_text='Can be either email or phone number', max_length=200, verbose_name='ایمیل / شماره تماس')),
|
||||
('description', models.TextField(verbose_name='توضیحات')),
|
||||
('category', models.CharField(choices=[('همکاری', 'همکاری'), ('فروش', 'فروش'), ('پشتیبانی', 'پشتیبانی'), ('درخواست مشاور', 'درخواست مشاور'), ('سایر', 'سایر')], max_length=50, verbose_name='دسته\u200cبندی')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ایجاد')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='تاریخ بروزرسانی')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'تماس با ما',
|
||||
'verbose_name_plural': 'تماس\u200cهای با ما',
|
||||
'ordering': ['-created_at'],
|
||||
'indexes': [models.Index(fields=['-created_at'], name='api_contact_created_d03dd7_idx'), models.Index(fields=['category'], name='api_contact_categor_76ad37_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Composition',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, verbose_name='نام')),
|
||||
('description', models.TextField(verbose_name='توضیحات')),
|
||||
('image', models.ImageField(blank=True, null=True, upload_to='compositions/', verbose_name='تصویر')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ایجاد')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='تاریخ بروزرسانی')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'ترکیب',
|
||||
'verbose_name_plural': 'ترکیبات',
|
||||
'ordering': ['-created_at'],
|
||||
'indexes': [models.Index(fields=['-created_at'], name='api_composi_created_ac63fb_idx')],
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Campaign',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=200, verbose_name='نام')),
|
||||
('description', models.TextField(verbose_name='توضیحات')),
|
||||
('start_time', models.DateTimeField(verbose_name='زمان شروع')),
|
||||
('end_time', models.DateTimeField(verbose_name='زمان پایان')),
|
||||
('image', models.ImageField(blank=True, null=True, upload_to='campaigns/', verbose_name='تصویر')),
|
||||
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ایجاد')),
|
||||
('updated_at', models.DateTimeField(auto_now=True, verbose_name='تاریخ بروزرسانی')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'کمپین',
|
||||
'verbose_name_plural': 'کمپین\u200cها',
|
||||
'ordering': ['-start_time'],
|
||||
'indexes': [models.Index(fields=['-start_time'], name='api_campaig_start_t_c79399_idx'), models.Index(fields=['end_time'], name='api_campaig_end_tim_d3f700_idx')],
|
||||
},
|
||||
),
|
||||
]
|
||||
0
api/migrations/__init__.py
Normal file
0
api/migrations/__init__.py
Normal file
113
api/models.py
Normal file
113
api/models.py
Normal file
@@ -0,0 +1,113 @@
|
||||
from django.db import models
|
||||
from django.core.validators import EmailValidator
|
||||
from django.utils import timezone
|
||||
|
||||
|
||||
class ContactUs(models.Model):
|
||||
"""
|
||||
Contact Us model for handling contact form submissions.
|
||||
"""
|
||||
CATEGORY_CHOICES = [
|
||||
('همکاری', 'همکاری'),
|
||||
('فروش', 'فروش'),
|
||||
('پشتیبانی', 'پشتیبانی'),
|
||||
('درخواست مشاور', 'درخواست مشاور'),
|
||||
('سایر', 'سایر'),
|
||||
]
|
||||
|
||||
name = models.CharField(max_length=200, verbose_name='نام و نام خانوادگی')
|
||||
email_or_phone = models.CharField(
|
||||
max_length=200,
|
||||
verbose_name='ایمیل / شماره تماس',
|
||||
help_text='Can be either email or phone number'
|
||||
)
|
||||
description = models.TextField(verbose_name='توضیحات')
|
||||
category = models.CharField(
|
||||
max_length=50,
|
||||
choices=CATEGORY_CHOICES,
|
||||
verbose_name='دستهبندی'
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ایجاد')
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name='تاریخ بروزرسانی')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'تماس با ما'
|
||||
verbose_name_plural = 'تماسهای با ما'
|
||||
ordering = ['-created_at']
|
||||
indexes = [
|
||||
models.Index(fields=['-created_at']),
|
||||
models.Index(fields=['category']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return f"{self.name} - {self.category}"
|
||||
|
||||
|
||||
class Composition(models.Model):
|
||||
"""
|
||||
Composition model for storing composition information.
|
||||
"""
|
||||
name = models.CharField(max_length=200, verbose_name='نام')
|
||||
description = models.TextField(verbose_name='توضیحات')
|
||||
image = models.ImageField(
|
||||
upload_to='compositions/',
|
||||
verbose_name='تصویر',
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ایجاد')
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name='تاریخ بروزرسانی')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'ترکیب'
|
||||
verbose_name_plural = 'ترکیبات'
|
||||
ordering = ['-created_at']
|
||||
indexes = [
|
||||
models.Index(fields=['-created_at']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class Campaign(models.Model):
|
||||
"""
|
||||
Campaign model for storing campaign information.
|
||||
"""
|
||||
name = models.CharField(max_length=200, verbose_name='نام')
|
||||
description = models.TextField(verbose_name='توضیحات')
|
||||
start_time = models.DateTimeField(verbose_name='زمان شروع')
|
||||
end_time = models.DateTimeField(verbose_name='زمان پایان')
|
||||
image = models.ImageField(
|
||||
upload_to='campaigns/',
|
||||
verbose_name='تصویر',
|
||||
null=True,
|
||||
blank=True
|
||||
)
|
||||
created_at = models.DateTimeField(auto_now_add=True, verbose_name='تاریخ ایجاد')
|
||||
updated_at = models.DateTimeField(auto_now=True, verbose_name='تاریخ بروزرسانی')
|
||||
|
||||
class Meta:
|
||||
verbose_name = 'کمپین'
|
||||
verbose_name_plural = 'کمپینها'
|
||||
ordering = ['-start_time']
|
||||
indexes = [
|
||||
models.Index(fields=['-start_time']),
|
||||
models.Index(fields=['end_time']),
|
||||
]
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
def is_active(self):
|
||||
"""Check if campaign is currently active."""
|
||||
now = timezone.now()
|
||||
return self.start_time <= now <= self.end_time
|
||||
|
||||
def is_upcoming(self):
|
||||
"""Check if campaign is upcoming."""
|
||||
return timezone.now() < self.start_time
|
||||
|
||||
def is_ended(self):
|
||||
"""Check if campaign has ended."""
|
||||
return timezone.now() > self.end_time
|
||||
126
api/serializers.py
Normal file
126
api/serializers.py
Normal file
@@ -0,0 +1,126 @@
|
||||
from rest_framework import serializers
|
||||
from .models import ContactUs, Composition, Campaign
|
||||
|
||||
|
||||
class ContactUsSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
Serializer for ContactUs model.
|
||||
"""
|
||||
class Meta:
|
||||
model = ContactUs
|
||||
fields = ['id', 'name', 'email_or_phone', 'description', 'category',
|
||||
'created_at', 'updated_at']
|
||||
read_only_fields = ['id', 'created_at', 'updated_at']
|
||||
|
||||
def validate_email_or_phone(self, value):
|
||||
"""Validate that the field contains either email or phone."""
|
||||
if not value or len(value.strip()) == 0:
|
||||
raise serializers.ValidationError("Email or phone number is required.")
|
||||
return value.strip()
|
||||
|
||||
def validate_name(self, value):
|
||||
"""Validate name field."""
|
||||
if not value or len(value.strip()) == 0:
|
||||
raise serializers.ValidationError("Name is required.")
|
||||
return value.strip()
|
||||
|
||||
def validate_description(self, value):
|
||||
"""Validate description field."""
|
||||
if not value or len(value.strip()) == 0:
|
||||
raise serializers.ValidationError("Description is required.")
|
||||
return value.strip()
|
||||
|
||||
|
||||
class CompositionSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
Serializer for Composition model.
|
||||
"""
|
||||
image_url = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Composition
|
||||
fields = ['id', 'name', 'description', 'image', 'image_url',
|
||||
'created_at', 'updated_at']
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'image_url']
|
||||
|
||||
def get_image_url(self, obj):
|
||||
"""Get the full URL of the image."""
|
||||
if obj.image:
|
||||
request = self.context.get('request')
|
||||
if request:
|
||||
return request.build_absolute_uri(obj.image.url)
|
||||
return obj.image.url
|
||||
return None
|
||||
|
||||
def validate_name(self, value):
|
||||
"""Validate name field."""
|
||||
if not value or len(value.strip()) == 0:
|
||||
raise serializers.ValidationError("Name is required.")
|
||||
return value.strip()
|
||||
|
||||
def validate_description(self, value):
|
||||
"""Validate description field."""
|
||||
if not value or len(value.strip()) == 0:
|
||||
raise serializers.ValidationError("Description is required.")
|
||||
return value.strip()
|
||||
|
||||
|
||||
class CampaignSerializer(serializers.ModelSerializer):
|
||||
"""
|
||||
Serializer for Campaign model.
|
||||
"""
|
||||
image_url = serializers.SerializerMethodField()
|
||||
is_active = serializers.SerializerMethodField()
|
||||
is_upcoming = serializers.SerializerMethodField()
|
||||
is_ended = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Campaign
|
||||
fields = ['id', 'name', 'description', 'start_time', 'end_time',
|
||||
'image', 'image_url', 'is_active', 'is_upcoming', 'is_ended',
|
||||
'created_at', 'updated_at']
|
||||
read_only_fields = ['id', 'created_at', 'updated_at', 'image_url',
|
||||
'is_active', 'is_upcoming', 'is_ended']
|
||||
|
||||
def get_image_url(self, obj):
|
||||
"""Get the full URL of the image."""
|
||||
if obj.image:
|
||||
request = self.context.get('request')
|
||||
if request:
|
||||
return request.build_absolute_uri(obj.image.url)
|
||||
return obj.image.url
|
||||
return None
|
||||
|
||||
def get_is_active(self, obj):
|
||||
"""Get campaign active status."""
|
||||
return obj.is_active()
|
||||
|
||||
def get_is_upcoming(self, obj):
|
||||
"""Get campaign upcoming status."""
|
||||
return obj.is_upcoming()
|
||||
|
||||
def get_is_ended(self, obj):
|
||||
"""Get campaign ended status."""
|
||||
return obj.is_ended()
|
||||
|
||||
def validate(self, data):
|
||||
"""Validate that end_time is after start_time."""
|
||||
if 'start_time' in data and 'end_time' in data:
|
||||
if data['end_time'] <= data['start_time']:
|
||||
raise serializers.ValidationError({
|
||||
'end_time': 'End time must be after start time.'
|
||||
})
|
||||
return data
|
||||
|
||||
def validate_name(self, value):
|
||||
"""Validate name field."""
|
||||
if not value or len(value.strip()) == 0:
|
||||
raise serializers.ValidationError("Name is required.")
|
||||
return value.strip()
|
||||
|
||||
def validate_description(self, value):
|
||||
"""Validate description field."""
|
||||
if not value or len(value.strip()) == 0:
|
||||
raise serializers.ValidationError("Description is required.")
|
||||
return value.strip()
|
||||
|
||||
3
api/tests.py
Normal file
3
api/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
13
api/urls.py
Normal file
13
api/urls.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from django.urls import path, include
|
||||
from rest_framework.routers import DefaultRouter
|
||||
from .views import ContactUsViewSet, CompositionViewSet, CampaignViewSet
|
||||
|
||||
router = DefaultRouter()
|
||||
router.register(r'contact-us', ContactUsViewSet, basename='contact-us')
|
||||
router.register(r'compositions', CompositionViewSet, basename='composition')
|
||||
router.register(r'campaigns', CampaignViewSet, basename='campaign')
|
||||
|
||||
urlpatterns = [
|
||||
path('', include(router.urls)),
|
||||
]
|
||||
|
||||
114
api/views.py
Normal file
114
api/views.py
Normal file
@@ -0,0 +1,114 @@
|
||||
from rest_framework import viewsets, status
|
||||
from rest_framework.decorators import action
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||
from django.db.models import Q
|
||||
from django.utils import timezone
|
||||
from .models import ContactUs, Composition, Campaign
|
||||
from .serializers import ContactUsSerializer, CompositionSerializer, CampaignSerializer
|
||||
|
||||
|
||||
class ContactUsViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
ViewSet for ContactUs model.
|
||||
Provides CRUD operations for contact form submissions.
|
||||
"""
|
||||
queryset = ContactUs.objects.all()
|
||||
serializer_class = ContactUsSerializer
|
||||
permission_classes = [AllowAny] # Allow anyone to submit contact forms
|
||||
|
||||
def get_permissions(self):
|
||||
"""
|
||||
Override to allow GET (list, retrieve) and POST (create) for anyone.
|
||||
"""
|
||||
if self.action in ['list', 'retrieve', 'create']:
|
||||
return [AllowAny()]
|
||||
return [IsAuthenticated()]
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def by_category(self, request):
|
||||
"""Get contacts filtered by category."""
|
||||
category = request.query_params.get('category', None)
|
||||
if category:
|
||||
contacts = self.queryset.filter(category=category)
|
||||
serializer = self.get_serializer(contacts, many=True)
|
||||
return Response(serializer.data)
|
||||
return Response(
|
||||
{'error': 'Category parameter is required.'},
|
||||
status=status.HTTP_400_BAD_REQUEST
|
||||
)
|
||||
|
||||
|
||||
class CompositionViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
ViewSet for Composition model.
|
||||
Provides CRUD operations for compositions.
|
||||
"""
|
||||
queryset = Composition.objects.all()
|
||||
serializer_class = CompositionSerializer
|
||||
permission_classes = [AllowAny] # Public read access
|
||||
|
||||
def get_permissions(self):
|
||||
"""
|
||||
Override to allow GET (list, retrieve) and POST (create) for anyone.
|
||||
"""
|
||||
if self.action in ['list', 'retrieve', 'create']:
|
||||
return [AllowAny()]
|
||||
return [IsAuthenticated()]
|
||||
|
||||
def get_serializer_context(self):
|
||||
"""Add request to serializer context for image URL generation."""
|
||||
context = super().get_serializer_context()
|
||||
context['request'] = self.request
|
||||
return context
|
||||
|
||||
|
||||
class CampaignViewSet(viewsets.ModelViewSet):
|
||||
"""
|
||||
ViewSet for Campaign model.
|
||||
Provides CRUD operations for campaigns.
|
||||
"""
|
||||
queryset = Campaign.objects.all()
|
||||
serializer_class = CampaignSerializer
|
||||
permission_classes = [AllowAny] # Public read access
|
||||
|
||||
def get_permissions(self):
|
||||
"""
|
||||
Override to allow GET (list, retrieve) and POST (create) for anyone.
|
||||
"""
|
||||
if self.action in ['list', 'retrieve', 'create', 'active', 'upcoming', 'ended']:
|
||||
return [AllowAny()]
|
||||
return [IsAuthenticated()]
|
||||
|
||||
def get_serializer_context(self):
|
||||
"""Add request to serializer context for image URL generation."""
|
||||
context = super().get_serializer_context()
|
||||
context['request'] = self.request
|
||||
return context
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def active(self, request):
|
||||
"""Get all currently active campaigns."""
|
||||
now = timezone.now()
|
||||
active_campaigns = self.queryset.filter(
|
||||
start_time__lte=now,
|
||||
end_time__gte=now
|
||||
)
|
||||
serializer = self.get_serializer(active_campaigns, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def upcoming(self, request):
|
||||
"""Get all upcoming campaigns."""
|
||||
now = timezone.now()
|
||||
upcoming_campaigns = self.queryset.filter(start_time__gt=now)
|
||||
serializer = self.get_serializer(upcoming_campaigns, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
@action(detail=False, methods=['get'])
|
||||
def ended(self, request):
|
||||
"""Get all ended campaigns."""
|
||||
now = timezone.now()
|
||||
ended_campaigns = self.queryset.filter(end_time__lt=now)
|
||||
serializer = self.get_serializer(ended_campaigns, many=True)
|
||||
return Response(serializer.data)
|
||||
Reference in New Issue
Block a user