FK in Form automatisch füllen und anschließend redirect zur ursprünglichen URL

Django, Flask, Bottle, WSGI, CGI…
Antworten
gospat83
User
Beiträge: 36
Registriert: Dienstag 21. September 2021, 14:25

Hallo zusammen,

ich habe folgendes Szenario. Ich bin auf der folgenden URL:

Code: Alles auswählen

path('bgr-cockpit/verantwortlichkeit/<int:pk>/', VerantwortlichkeitDetailView.as_view(), name="verantwortlichkeit"),
Der PK meines Models Verantwortlichkeit ist dadurch ja bekannt (URL z.B.: bgr-cockpit/verantwortlichkeit/1/). Nun möchte ich auf dieser Detailpage ein neues Problem erzeugen lassen über das folgende Form:

Code: Alles auswählen

class ProblemForm(forms.ModelForm):
    class Meta:
        model = Problem
        fields = ['zusammenfassung', 'beschreibung']
        widgets = {
            'zusammenfassung': forms.TextInput(attrs={'class': 'form-control'}),
            'beschreibung': forms.Textarea(attrs={'class': 'form-control'}),
        }
Das Problem Model sieht wie folgt aus:

Code: Alles auswählen

class Problem(models.Model):
    verantwortlichkeit = models.ForeignKey(Verantwortlichkeit, on_delete=models.CASCADE)
    zusammenfassung = models.CharField(max_length=100)
    beschreibung = models.TextField()
    ist_geloest = models.BooleanField(verbose_name='gelöst', default=False)
    erstellt_am = models.DateTimeField(auto_now_add=True, editable=False)
    geaendert_am = models.DateTimeField(auto_now=True, editable=False)

    class Meta:
        verbose_name_plural = 'Probleme'
    
    def __str__(self):
        return f'{self.verantwortlichkeit} | {self.zusammenfassung}'
Ich möchte jetzt aber irgendwie direkt den PK aus der URL mitgeben, da ich ja bei der Anlage eines Problems die verantwortlichkeit_id mit angeben muss. Kann man das irgendwie direkt als Parameter übergeben? Wenn ich das Feld als models.Select anlege und im Form dann manuell setze, wird mein Datensatz zumindest erzeugt. Allerdings bekomme ich direkt danach eine Fehlermeldung, sinngemäß "URL nicht korrekt konfiguriert". Muss ich eine success_url angeben? Bei meinem anderen Form musste ich das nicht.

Viele Grüße
gospat
Benutzeravatar
noisefloor
User
Beiträge: 4035
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

wie gezeigt kann das in der Tat nicht funktionieren, weil das Modell "Problem" ja einen Wert für "verantwortlichkeit" erwartet - das du aber in deinem Formular gar nicht drin.

Zeig' bitte auch mal den zugehörigen View sowie das zugehörige Template.

Gruß, noisefloor
gospat83
User
Beiträge: 36
Registriert: Dienstag 21. September 2021, 14:25

Hallo,

Entschuldigung erst einmal für meine späte Rückmeldung, aber ich habe in den letzten Wochen versucht mein Wissen über Django zu erweitern. Ich habe also noch einmal komplett von vorne angefangen und mich versucht auch an der Doku von djangoproject zu orientieren. Viele Dinge sind mir dabei viel bewusster geworden. Bei diesem Problem bin ich aber wieder angekommen.

Ich schildere noch einmal detailliert die Ausgangslage. Meine URLs sehen wie folgt aus:

Code: Alles auswählen

urlpatterns = [
    path('', views.responsibilities_overview, name='responsibilities-overview'),
    path('responsibility-details/<int:id>/', views.responsibility_details, name='responsibility-details'),
]
Ich kann also die Details einer Verantwortlichkeit (responsibility) über den View responsibility_details erreichen. Dafür gebe ich ja die ID als Parameter mit. Nehmen wir mal an ich befinde mich nun auf der Seite .../responsibility-details/1/. Auf dieser Seite soll der User jetzt die Möglichkeit haben Probleme und Lösungen zu erfassen, jeweils über ein eigenes Formular. Für die Anlage eines Problems wird die ID des Responsibility model benötigt (die 1 aus der URL). Für die spätere Anlage einer Lösung zu diesem Problem benötige ich wiederum die ID des Problems.

Wie und an welcher Stelle reiche ich diese Informationen jetzt am besten an die Formulare weiter? Meine views.py Datei (nur der relevante Ausschnitt) sieht wie folgt aus:

Code: Alles auswählen

def responsibility_details(request, id):
    responsibility = get_object_or_404(Responsibility, id=id)

    bottleneck_parts = BottleneckPart.objects.filter(cost_bearer=responsibility.project.cost_bearer).filter(department=responsibility.team.department.name). \
        filter(system=responsibility.system.number)
    
    drawings = Drawing.objects.filter(cost_bearer=responsibility.project.cost_bearer).filter(department=responsibility.team.department.name). \
        filter(system=responsibility.system.number).order_by('drawing_number').values()
    drawings_filter = DrawingFilter(request.GET, queryset=drawings)
    drawings = drawings_filter.qs

    drawings_visibility = Drawing.objects.filter(cost_bearer=responsibility.project.cost_bearer).filter(department=responsibility.team.department.name). \
        filter(system=responsibility.system.number).order_by('drawing_number').values()
    
    project_rounds = Meeting.objects.filter(cost_bearer=responsibility.project.cost_bearer).filter(system=responsibility.system.number). \
        filter(title='Projektrunde').order_by('description', 'subject', 'item', 'date', 'responsibility').values()
    
    working_meetings = Meeting.objects.filter(cost_bearer=responsibility.project.cost_bearer).filter(system=responsibility.system.number). \
        filter(title='Working Meeting').order_by('description', 'subject', 'item', 'date', 'responsibility').values()
    
    context = {'responsibility': responsibility, 'bottleneck_parts': bottleneck_parts, 'drawings': drawings \
                , 'project_rounds': project_rounds, 'working_meetings': working_meetings, 'drawings_filter': drawings_filter \
                , 'drawings_visibility': drawings_visibility}
    
    return render(request, 'systemcockpit/responsibility_details.html', context)
Das Formular für das Problem habe ich noch nicht "richtig" erstellt, es dient also eher als Beispiel:

Code: Alles auswählen

from django import forms


class ProblemForm(forms.Form):
    # responsibility_id (wie bekomme ich diese ID ins Formular?)
    summary = forms.CharField()
    description = forms.CharField()
Der relevante Part der models.py sieht wie folgt aus:

Code: Alles auswählen

class Problem(models.Model):
    responsibility = models.ForeignKey(Responsibility, on_delete=models.CASCADE, verbose_name='Verantwortlichkeit', related_name='problems')
    summary = models.CharField(max_length=150, verbose_name='Zusammenfassung')
    description = models.TextField(verbose_name='Beschreibung')
    created_at = models.DateTimeField(auto_now_add=True, editable=False, blank=True, verbose_name='Erstelldatum')
    updated_at = models.DateTimeField(auto_now=True, editable=False, blank=True, verbose_name='Änderungsdatum')
    user = models.ForeignKey('auth.User', on_delete=models.SET_DEFAULT, default=1, editable=False, verbose_name='Ersteller / Änderer')

    class Meta:
        verbose_name = 'Problem'
        verbose_name_plural = 'Probleme'

    def __str__(self):
        return f'{self.responsibility} | {self.summary}'
    

class Solution(models.Model):
    problem = models.OneToOneField(Problem, on_delete=models.CASCADE, verbose_name='Problem', related_name='solutions')
    summary = models.CharField(max_length=150, verbose_name='Zusammenfassung')
    description = models.TextField(verbose_name='Beschreibung')
    created_at = models.DateTimeField(auto_now_add=True, editable=False, blank=True, verbose_name='Erstelldatum')
    updated_at = models.DateTimeField(auto_now=True, editable=False, blank=True, verbose_name='Änderungsdatum')
    user = models.ForeignKey('auth.User', on_delete=models.SET_DEFAULT, default=1, editable=False, verbose_name='Ersteller / Änderer')

    class Meta:
        verbose_name = 'Maßnahme'
        verbose_name_plural = 'Maßnahmen'

    def __str__(self):
        return f'{self.problem} | {self.summary}'  
Ich stelle mir es so vor, dass ich in meinem Template Buttons anlege, einer "Problem erstellen" und einer "Lösung erstellen". Klickt man auf einen der beiden, soll man zum entsprechenden Formular gelangen, sprich zu einem eigenen Template dafür.

Nach dem Erstellen eines Problems bzw. einer Lösung soll man zurück zur ursprünglichen URL gelangen (die oben genannte URL).

Für ein paar konkrete Tipps wäre ich wirklich sehr dankbar. Ich bin "leider" immer noch am Anfang meiner "Programmierkarriere", bitte seht mir also nach, wenn mich der Verweis auf die Doku oder ähnliches nicht immer weiterbringt. Vieles davon ist für einen Anfänger auch nach dem Durchlesen immer noch sehr schwierig zu verstehen, wenn man damit nicht täglich zu tun hat. Sollten Infos fehlen, liefere ich diese natürlich gerne nach.

Viele Grüße
gospat
gospat83
User
Beiträge: 36
Registriert: Dienstag 21. September 2021, 14:25

Ich habe gerade noch ein paar Anpassungen gemacht um es noch deutlicher zu machen.

So sieht meine forms.py Datei jetzt aus:

Code: Alles auswählen

from django.forms import ModelForm
from .models import Problem

class ProblemForm(ModelForm):
    class Meta:
        model = Problem
        fields = '__all__'
Das ist der Ausschnitt aus der urls.py:

Code: Alles auswählen

urlpatterns = [
    path('', views.responsibilities_overview, name='responsibilities-overview'),
    path('responsibility-details/<int:id>/', views.responsibility_details, name='responsibility-details'),
    path('problem-create/', views.problem_create, name='problem-create'),
    path('problem-update/<int:id>/', views.problem_update, name='problem-update'),
]
Und hier die views.py:

Code: Alles auswählen

def problem_create(request):
    problem_form = ProblemForm()
    if request.method == 'POST':
        problem_form = ProblemForm(request.POST)
        if problem_form.is_valid:
            problem_form.save()
            return redirect('/') # redirect soll eigentlich zu .../responsibilitiy-details/<ID>

    context = {'problem_form': problem_form}
    
    return render(request, 'systemcockpit/problem_form.html', context)


def problem_update(request, id):
    problem = Problem.objects.get(id=id)
    problem_form = ProblemForm(instance=problem)

    if request.method == 'POST':
        problem_form = ProblemForm(request.POST, instance=problem)
        if problem_form.is_valid:
            problem_form.save()
            return redirect('/') # redirect soll eigentlich zu .../responsibilitiy-details/<ID>

    context = {'problem_form': problem_form}
    
    return render(request, 'systemcockpit/problem_form.html', context)
Und die enrsprechenden Aufrufe im Template:

Code: Alles auswählen

<a class="btn btn-primary" href="{% url 'problem-create' %}">Problem erstellen</a>
sowie

Code: Alles auswählen

<a class="btn btn-primary" href="{% url 'problem-update' problem.id %}">Problem ändern</a>    
Wenn ich jetzt den Button zum Problem erstellen klicke, öffnet sich wie gewünscht das Formular. Allerdings kann ich jetzt logischerweise noch die Verantwortlichkeit (responsibility) aus einer Dropdownliste auswählen. Das soll nicht mehr möglich sein, die soll automatisch im Hintergrund befüllt werden. Ich würde ebenfalls gerne den eingeloggten User ermitteln und ebenfalls automatisch befüllen lassen. Dass ich die forms.py anpassen muss beim __all__ vermute ich mal ganz stark, aber da ich noch nicht weiß wie ich mein Problem löse, habe ich erst einmal alles so gelassen wie es ist.
Antworten