jonashaag / django-addanother

"Add another" buttons outside the Django admin
http://django-addanother.readthedocs.org/
ISC License
50 stars 18 forks source link

AddAnotherWidgets not filled #42

Closed Joris-Pieters closed 4 years ago

Joris-Pieters commented 4 years ago

Thanks for your great work.

I'm not sure if this is a bug or a mistake on my side (more likely), but when I load an existing instance in a form the AddAnotherWidgets are not filled with the current value. The "normal" fields are filled however.

I based my code on the example app, but that only has a submit option and does not let you load an existing instance.

Any idea what could be going wrong?

jonashaag commented 4 years ago

Can you please post some code so it’s easier to understand.

Joris-Pieters commented 4 years ago

In my case I have a model "project" that contains a CharField "title", and a ForeignKey "company". For "company" I want to use the AddAnotherWidget so the user can add companies (there's more to a "project" of course, but I try to limit the code to only what is relevant):

models:

from django.db import models

class Project(models.Model):

    title = models.CharField(max_length = 255)
    company = models.ForeignKey(Company, on_delete = models.DO_NOTHING, blank = True, null = True)

    def __str__(self):
        if self.start_date is None:
            return f"{self.company.__str__()}: {self.title.__str__()} "
        else:
            return f"{self.company.__str__()}: {self.title.__str__()} ({str(self.start_date.year)})"

    class Meta:
        ordering = [Lower('company'), Lower('title')]

class Company(models.Model):

    name = models.CharField(max_length = 255, unique = True, blank = False)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name_plural = "Companies"
        ordering = [Lower('name')]      

views (project_view is function based as this is what I knew when I started while view for company is class based as this is a requirement for AddAnother if I understood well):

from django.shortcuts import render
from django.http import HttpResponse
from django.contrib import messages
from django_addanother.views import CreatePopupMixin

from ..forms import *
from ..models import *

def project_view(request, pk):

    if request.user.is_authenticated:

        project = Project.objects.get(pk = pk)
        request.user.last_watched_project = pk
        request.user.save()

        if request.method == 'GET':        
            form = ProjectForm(initial = project.__dict__)
            context = {
                'form': form,
                'jquery': 'admin/js/vendor/jquery/jquery.js',
            }
            return render(request, "main/project.html", context)

        if request.method == 'POST':
            form = ProjectForm(request.POST)
            context = {
                'form': form,
                'jquery': 'admin/js/vendor/jquery/jquery.js',
            }

            if not request.user.is_superuser:
                messages.error(request, "You do not have the proper rights to change this.")
                return render(request, "main/project.html", context)

            if form.is_valid():
                project = form.save()
                messages.success(request, "Project saved.")
                return render(request, "main/projects.html")
            else:
                messages.error(request, "One or more required fields are empty.")
                return render(request, "main/project.html", context)

class CreateCompany(CreatePopupMixin, generic.CreateView):

    model = Company
    fields = ['name']

forms:

from django import forms
from django_addanother.widgets import AddAnotherWidgetWrapper

from main.models import *

class ProjectForm(forms.ModelForm):

     class Meta:
        model = Project
        fields = ('title', 'company')
        widgets = {
            'company': AddAnotherWidgetWrapper(
                forms.Select(),
                '/company',
            ),
        }

class CompanyForm(forms.ModelForm):

    class Meta:
        model = Project
        fields = '__all__'

urls:

from django.conf.urls.static import static
from django.urls import path

from references import settings
from . import views

app_name = 'main'

urlpatterns = [
    path("", views.home, name = "home"),
    path("project/<int:pk>/",  views.project_view, name = "project_view"),    
    path("company", views.company_view.CreateCompany.as_view(), name = "company_view"),
    ] + static(settings.MEDIA_URL, document_root = settings.MEDIA_ROOT)

Problem: Let's say I made a project with name = "first project" and company = "EvilCorp". If I check in admin this is saved properly, even if I created "EvilCorp" on the spot using the AddAnotherWidget (so that is working great!). When I load this project back to a form for editing however the field name correctly contains "first project", while company gives the default value of "------". Same goes for other "classic" widgets versus AddAnotherWidgets.

Thanks for your support!

Joris-Pieters commented 4 years ago

My bad, this wasn't an issue with AddAnotherWidgets, but my very own mistake. in the views form = ProjectForm(initial = project.__dict__) should be form = ProjectForm(instance = project)