feat: Add basic flow to add adoption notices

This commit is contained in:
moanos [he/him] 2024-03-19 18:18:55 +01:00
parent dda400f3ba
commit 8488768687
15 changed files with 94 additions and 236 deletions

View File

@ -12,4 +12,7 @@ debug=True
backend=sqlite3
name=notfellchen
[locations]
media=./media

View File

@ -11,15 +11,6 @@ class Command(BaseCommand):
@staticmethod
def populate_db():
rat1 = baker.make_recipe(
'fellchensammlung.rat'
)
rat2 = baker.make_recipe(
'fellchensammlung.rat'
)
cat = baker.make_recipe(
'fellchensammlung.cat'
)
rescue1 = baker.make_recipe(
'fellchensammlung.rescue_org'
)
@ -27,9 +18,13 @@ class Command(BaseCommand):
'fellchensammlung.rescue_org'
)
baker.make(AdoptionNotice, name="Vermittung1", animals=[rat1, rat2], organization=rescue1)
adoption1 = baker.make(AdoptionNotice, name="Vermittung1", organization=rescue1)
baker.make(AdoptionNotice, name="Vermittung2", animals=[cat], organization=rescue2)
adoption2 = baker.make(AdoptionNotice, name="Vermittung2", organization=rescue2)
rat1 = baker.make(Animal, name="Rat1", adoption_notice=adoption1)
rat2 = baker.make(Animal, name="Rat2", adoption_notice=adoption1)
cat1 = baker.make(Animal, name="Cat1", adoption_notice=adoption1)
User.objects.create_user('test', password='foobar')
User.objects.create_superuser(username="admin", password="admin")

View File

@ -1,73 +0,0 @@
# Generated by Django 5.0.3 on 2024-03-17 22:05
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Location',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('postcode', models.CharField(max_length=200)),
('country', models.CharField(choices=[('DE', 'Germany'), ('AT', 'Austria'), ('CH', 'Switzerland')], max_length=20)),
('description', models.TextField(blank=True, null=True, verbose_name='Description')),
],
),
migrations.CreateModel(
name='Species',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(help_text='Enter a animal species', max_length=200, verbose_name='Name')),
],
options={
'verbose_name': 'Species',
'verbose_name_plural': 'Species',
},
),
migrations.CreateModel(
name='RescueOrganization',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.CharField(max_length=200)),
('trusted', models.BooleanField(default=False, verbose_name='Trusted')),
('instagram', models.URLField(blank=True, null=True, verbose_name='Instagram profile')),
('facebook', models.URLField(blank=True, null=True, verbose_name='Facebook profile')),
('fediverse_profile', models.URLField(blank=True, null=True, verbose_name='Fediverse profile')),
('website', models.URLField(blank=True, null=True, verbose_name='Website')),
('location', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fellchensammlung.location')),
],
),
migrations.CreateModel(
name='AdoptionNotice',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created_at', models.DateField(verbose_name='Created at')),
('searching_since', models.DateField(verbose_name='Searching for a home since')),
('name', models.CharField(max_length=200)),
('description', models.TextField(blank=True, null=True, verbose_name='Description')),
('further_information', models.URLField(blank=True, null=True, verbose_name='Link to further information')),
('group_only', models.BooleanField(default=False, verbose_name='Only group adoption')),
('organization', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='fellchensammlung.rescueorganization', verbose_name='Organization')),
],
),
migrations.CreateModel(
name='Animal',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('date_of_birth', models.DateField(blank=True, null=True, verbose_name='Date of birth')),
('name', models.CharField(max_length=200)),
('description', models.TextField(blank=True, null=True, verbose_name='Description')),
('adoption_notice', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='fellchensammlung.adoptionnotice')),
('species', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='fellchensammlung.species')),
],
),
]

View File

@ -1,22 +0,0 @@
# Generated by Django 5.0.3 on 2024-03-18 09:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('fellchensammlung', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='animal',
name='adoption_notice',
),
migrations.AddField(
model_name='adoptionnotice',
name='animals',
field=models.ManyToManyField(to='fellchensammlung.animal'),
),
]

View File

@ -1,24 +0,0 @@
# Generated by Django 5.0.3 on 2024-03-18 13:25
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('fellchensammlung', '0002_remove_animal_adoption_notice_adoptionnotice_animals'),
]
operations = [
migrations.CreateModel(
name='MarkdownContent',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=100)),
('content', models.TextField()),
],
options={
'verbose_name_plural': 'Markdown content',
},
),
]

View File

@ -1,36 +0,0 @@
# Generated by Django 5.0.3 on 2024-03-18 16:08
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('fellchensammlung', '0003_markdowncontent'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Image',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=200)),
('image', models.ImageField(upload_to='images')),
('alt_text', models.TextField(max_length=2000)),
('uploaded_by', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name='adoptionnotice',
name='photos',
field=models.ManyToManyField(blank=True, to='fellchensammlung.image'),
),
migrations.AddField(
model_name='animal',
name='photos',
field=models.ManyToManyField(blank=True, to='fellchensammlung.image'),
),
]

View File

@ -1,19 +0,0 @@
# Generated by Django 5.0.3 on 2024-03-19 04:50
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('fellchensammlung', '0004_image_adoptionnotice_photos_animal_photos'),
]
operations = [
migrations.AddField(
model_name='animal',
name='sex',
field=models.CharField(choices=[('M_N', 'male_neutered'), ('M', 'male'), ('F_N', 'female_neutered'), ('F', 'female')], default='female', max_length=20),
preserve_default=False,
),
]

View File

@ -1,20 +0,0 @@
# Generated by Django 5.0.3 on 2024-03-19 04:51
import datetime
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('fellchensammlung', '0005_animal_sex'),
]
operations = [
migrations.AlterField(
model_name='animal',
name='date_of_birth',
field=models.DateField(default=datetime.datetime(2024, 3, 19, 4, 51, 44, 367516, tzinfo=datetime.timezone.utc), verbose_name='Date of birth'),
preserve_default=False,
),
]

View File

@ -63,6 +63,25 @@ class RescueOrganization(models.Model):
website = models.URLField(null=True, blank=True, verbose_name=_('Website'))
class AdoptionNotice(models.Model):
def __str__(self):
return f"{self.name}"
created_at = models.DateField(verbose_name=_('Created at'), default=datetime.now)
searching_since = models.DateField(verbose_name=_('Searching for a home since'))
name = models.CharField(max_length=200)
description = models.TextField(null=True, blank=True, verbose_name=_('Description'))
organization = models.ForeignKey(RescueOrganization, blank=True, null=True, on_delete=models.SET_NULL,
verbose_name=_('Organization'))
further_information = models.URLField(null=True, blank=True, verbose_name=_('Link to further information'))
group_only = models.BooleanField(default=False, verbose_name=_('Only group adoption'))
photos = models.ManyToManyField(Image, blank=True)
@property
def animals_list(self):
return self.animals.all()
class Animal(models.Model):
MALE_NEUTERED = "M_N"
MALE = "M"
@ -81,6 +100,7 @@ class Animal(models.Model):
species = models.ForeignKey(Species, on_delete=models.PROTECT)
photos = models.ManyToManyField(Image, blank=True)
sex = models.CharField(max_length=20, choices=SEX_CHOICES, )
adoption_notice = models.ForeignKey(AdoptionNotice, on_delete=models.CASCADE)
def __str__(self):
return f"{self.name}"
@ -99,26 +119,6 @@ class Animal(models.Model):
return reverse('animal-detail', args=[str(self.id)])
class AdoptionNotice(models.Model):
def __str__(self):
return f"{self.name}"
created_at = models.DateField(verbose_name=_('Created at'))
searching_since = models.DateField(verbose_name=_('Searching for a home since'))
name = models.CharField(max_length=200)
description = models.TextField(null=True, blank=True, verbose_name=_('Description'))
organization = models.ForeignKey(RescueOrganization, blank=True, null=True, on_delete=models.SET_NULL,
verbose_name=_('Organization'))
further_information = models.URLField(null=True, blank=True, verbose_name=_('Link to further information'))
group_only = models.BooleanField(default=False, verbose_name=_('Only group adoption'))
animals = models.ManyToManyField(Animal)
photos = models.ManyToManyField(Image, blank=True)
@property
def animals_list(self):
return self.animals.all()
class MarkdownContent(models.Model):
"""
Base class to store markdown content

View File

@ -1,6 +0,0 @@
{% extends "fellchensammlung/base_generic.html" %}
{% load i18n %}
{% block content %}
Work in Progress: Vermitteln
{% endblock %}

View File

@ -0,0 +1,13 @@
{% extends "fellchensammlung/base_generic.html" %}
{% load i18n %}
{% block content %}
<h1>Vermitteln</h1>
Bitte mach dich zunächst mit unseren Regeln vertraut. Dann trage hier die ersten Informationen ein.
Du bekommst in einem weiteren Schritt die Möglichkeit einzelne Tiere zu deiner Vermittlung hinzuzufügen und Fotos hochzuladen.
<form method = "post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Weiter</button>
</form>
{% endblock %}

View File

@ -7,11 +7,17 @@ urlpatterns = [
# ex: /animal/5/
path("<int:animal_id>/", views.animal_detail, name="animal-detail"),
# ex: /adoption_notice/7/
path("<int:adoption_notice_id>/", views.adoption_notice_detail, name="adoption-notice-detail"),
path("vermittlung/<int:adoption_notice_id>/", views.adoption_notice_detail, name="adoption-notice-detail"),
# ex: /search/
path("suchen/", views.search, name="search"),
# ex: /vermitteln/
path("vermitteln/", views.add_adoption, name="add-adoption"),
# ex: vermitteln-tiere-hinzufügen/5
path("vermitteln-tiere-hinzufügen/<int:adoption_notice_id>",
views.add_animal_to_adoption,
name="add-animal-to-adoption"),
path("ueber-uns/", views.about, name="about"),
]

View File

@ -1,8 +1,10 @@
from django.shortcuts import render
from django.shortcuts import render, redirect
from django.http import HttpResponse
from django.urls import reverse
import markdown
from django.http import HttpResponse
from fellchensammlung.models import AdoptionNotice, MarkdownContent, Animal
from .forms import AdoptionNoticeForm, AnimalForm
def index(request):
@ -22,12 +24,42 @@ def animal_detail(request, animal_id):
context = {"animal": animal}
return render(request, 'fellchensammlung/detail_animal.html', context=context)
def search(request):
latest_adoption_list = AdoptionNotice.objects.order_by("-created_at")[:5]
context = {"adoption_notices": latest_adoption_list}
return render(request, 'fellchensammlung/search.html', context=context)
def add_adoption(request):
return render(request, 'fellchensammlung/add_adoption.html')
if request.method == 'POST':
form = AdoptionNoticeForm(request.POST, request.FILES)
if form.is_valid():
instance = form.save()
return redirect(reverse("add-animal-to-adoption", args=[instance.pk]))
else:
form = AdoptionNoticeForm()
return render(request, 'fellchensammlung/form_add_adoption.html', {'form': form})
def add_animal_to_adoption(request, adoption_notice_id):
if request.method == 'POST':
form = AnimalForm(request.POST, request.FILES)
if form.is_valid():
form.cleaned_data["adoption_notice_id"] = adoption_notice_id
instance = form.save(commit=False)
instance.adoption_notice_id = adoption_notice_id
instance.save()
if "button_add_another_animal" in request.POST:
return redirect(reverse("add-animal-to-adoption", args=[str(adoption_notice_id)]))
else:
return redirect(reverse("adoption-notice-detail", args=[str(adoption_notice_id)]))
else:
form = AnimalForm()
return render(request, 'fellchensammlung/form_add_animal_to_adoption.html', {'form': form})
def about(request):
md = markdown.Markdown(extensions=["fenced_code"])
@ -39,4 +71,3 @@ def about(request):
"fellchensammlung/about.html",
context=context
)

View File

@ -91,6 +91,9 @@ SEC_POLICY = config.get("security", "Policy",
""" LOCATIONS """
STATIC_ROOT = config.get("locations", "static", fallback="/notfellchen/static")
MEDIA_ROOT = config.get("locations", "media", fallback="/notfellchen/static")
MEDIA_URL = '/media/'
# see https://docs.djangoproject.com/en/3.2/ref/settings/#std-setting-ALLOWED_HOSTS
ALLOWED_HOSTS = [config.get("notfellchen", "host", fallback='*')]

View File

@ -16,8 +16,15 @@ Including another URLconf
"""
from django.contrib import admin
from django.urls import include, path
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path("", include("fellchensammlung.urls")),
path('admin/', admin.site.urls),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL,
document_root=settings.MEDIA_ROOT)