1

I want to offer users the possibility to create a new publication based on an existing publication. To do that, I want them to click a link to "basedview" that contains the id of the publication they want the new item to base on. There are two formsets for n:n relations included.

that should open a prefilled form with all fields prefield with the data from the publication it's based on. once the user has made changes as needed, it should then save a new publication and new relations for the fieldset - the latter being the difficult part of it.

So my question is - how can I load all corresponding formsets from the database and then delete all their pk but still keep the relation to the publication item?

Right now it is like this in the get method:

        self.object = None
        try:
            self.object = KombiPublikation.objects.get(pk=self.kwargs['pk'])
        except ObjectDoesNotExist:
            raise Http404("Keinen Output unter dieser PubID gefunden.")

        form = KombiPublikationForm(instance=self.object)
        pubspr_formset = KombiPublikationSpracheFormset(instance=self.object)
        pubpers_formset = KombiPublikationPersonFormset(instance=self.object)

But that ends up to be just an edit of the existing publication. I somehow have to delete the pk after I populated the formset or find a way to populate the formset differently. Any Ideas?

Thank you very much!

Here the full code excerpt:

class PublikationBasedView(PublikationCreateView):
    def get(self, request, *args, **kwargs):
        self.object = None
        try:
            self.object = KombiPublikation.objects.get(pk=self.kwargs['pk'])
        except ObjectDoesNotExist:
            raise Http404("Keinen Output unter dieser PubID gefunden.")

        #todo: delete the pk of all objects in forms in formset, else they stay the same and are also changed!!
        #fix: delete pk in objekt in order to save it as a new objekt - else based does not work at all!

        #self.object.pk=None

        form = KombiPublikationForm(instance=self.object)
        pubspr_formset = KombiPublikationSpracheFormset(instance=self.object)
        pubpers_formset = KombiPublikationPersonFormset(instance=self.object)



        return self.render_to_response(
            self.get_context_data(
                form=form,
                pubspr_formset=pubspr_formset,
                pubpers_formset=pubpers_formset,
            )
        )

#its based on this create view
class PublikationCreateView(LoginRequiredMixin, ShowNumberOfItems, CreateView):
    form_class = KombiPublikationForm
    template_name = 'output/pub_create.html'
    model = KombiPublikation

    def get(self, request, *args, **kwargs):
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        pubspr_formset = KombiPublikationSpracheFormset()
        pubpers_formset = KombiPublikationPersonFormset()

        return self.render_to_response(
            self.get_context_data(
                form=form,
                pubspr_formset=pubspr_formset,
                pubpers_formset=pubpers_formset
            )
        )

    def post(self, request, *args, **kwargs):
        self.object = None
        form_class = self.get_form_class()
        form = self.get_form(form_class)
        pubspr_formset = KombiPublikationSpracheFormset(self.request.POST)
        pubpers_formset = KombiPublikationPersonFormset(self.request.POST)

        if form.is_valid() and pubspr_formset.is_valid() and pubpers_formset.is_valid():
            return self.form_valid(form, pubspr_formset, pubpers_formset)
        else:
            return self.form_invalid(form, pubspr_formset, pubpers_formset)

    def get_success_msg(self):
        return 'Ihr Output wurde erfolgreich unter PubID {} angelegt. Speicherort: {}. <br>'.format(self.object.pk, self.object.status)

    def form_valid(self, form, pubspr_formset, pubpers_formset):
        """ Called if all forms are valid."""
        self.object = form.save()
        pubspr_formset.instance = self.object
        pubspr_formset.save()
        pubpers_formset.instance = self.object
        pubpers_formset.save()

        messages.success(self.request, self.get_success_msg())
        return redirect(self.get_success_url())

    def form_invalid(self, form, pubspr_formset, pubpers_formset):
        """ Called if whether a form is invalid. Re-renders data-filled forms and errors."""


        return self.render_to_response(
                        self.get_context_data(
                                form=form,
                                pubspr_formset=pubspr_formset,
                                pubpers_formset=pubpers_formset,

                        ))
teconomix
  • 227
  • 1
  • 3
  • 14

2 Answers2

1

Once you've set the Form's instance it's bound to that object. All updates will be to the object you passed.

Instead you need to set the Form's initial value

Use initial to declare the initial value of form fields at runtime. For example, you might want to fill in a username field with the username of the current session.

Then there's a utility at django.forms.models.model_to_dict that'll give you the dict you need for initial:

Returns a dict containing the data in instance suitable for passing as a Form's initial keyword argument.

So you'll need to do something like this:

from django.forms.models import model_to_dict

object = # Your code here...

# You don't want `id`. Possibly others...?
initial_data = model_to_dict(object, exclude=['id'])

form = YourFormClass(initial=initial_data)

# ...

Hopefully that helps.

Carlton Gibson
  • 7,278
  • 2
  • 36
  • 46
  • Hi there, think that is the right way. the problem ist, that it does not set foreign key values in the redered form. everything is there but the foreign keys, even though they are in the dict as values. Any idea why they don't show up in the form? – teconomix Dec 02 '16 at 13:32
  • Hmmm... It's probably that your Fields are ModelChoiceFields or such and the data coming out of model_to_dict isn't in the right format. You'll need to experiment and adjust it. – Carlton Gibson Dec 02 '16 at 13:42
  • I tried to load the fk object and replaced the value in the dict with the corresponding object. but still - the foreign key value does not show up in the rendered form... :( – teconomix Dec 02 '16 at 14:03
  • http://stackoverflow.com/questions/1336900/django-modelchoicefield-and-initial-value – Carlton Gibson Dec 02 '16 at 14:07
0

I solved the problem and since it was a bit more complicated then expected, I share my finding here - if someone finds a simpler solution feel free to add another comment

That is the final get method in the view:

def get(self, request, *args, **kwargs):
    self.object = None
    try:
        self.object = KombiPublikation.objects.get(pk=self.kwargs['pk'])
    except ObjectDoesNotExist:
        raise Http404("Keinen Output unter dieser PubID gefunden.")

    #load all form initials and render the form correctly - but save new objects
    #1. make sure the main publikation object is saved as a new object:
    self.object.pk = None
    self.object.erstellungsdatum = datetime.now()
    form = KombiPublikationForm(instance=self.object)

    #2. get the corresponding querysets for sprache and person:
    pubspr = KombiPublikationSprache.objects.filter(publikation=self.kwargs['pk'])
    pubpers = KombiPublikationPerson.objects.filter(publikation=self.kwargs['pk'])

    #make a list of dicts out of the querysets and delete pk id and fk relations
    pubspr_listofdicts = []
    for pubspr in pubspr:
        pubspr_dict= model_to_dict(pubspr)
        del pubspr_dict['id']
        del pubspr_dict['publikation']
        pubspr_listofdicts.append(pubspr_dict)

    pubpers_listofdicts = []
    for pubpers in pubpers:
        pubpers_dict=model_to_dict(pubpers)
        del pubpers_dict['id']
        del pubpers_dict['publikation']
        pubpers_listofdicts.append(pubpers_dict)

    #create new formsets with the right amount of forms (leng(obj_listofdicts)
    KombiPublikationSpracheFormset = inlineformset_factory(KombiPublikation,
                                                           KombiPublikationSprache,
                                                           form=KombiPublikationSpracheForm,
                                                           extra=len(pubspr_listofdicts),
                                                           can_delete=True,
                                                           can_order=True,
                                                           min_num=1,
                                                           validate_min=True)

    KombiPublikationPersonFormset = inlineformset_factory(
        KombiPublikation,
        KombiPublikationPerson,
        form=KombiPublikationPersonForm,
        extra=len(pubpers_listofdicts),
        can_delete=True,
        can_order=True,
        min_num=0,
        validate_min=True)

    #initiate the formset with initial data:
    pubspr_formset = KombiPublikationSpracheFormset(instance=self.object, initial=pubspr_listofdicts)
    pubpers_formset = KombiPublikationPersonFormset(instance=self.object, initial=pubpers_listofdicts)


    return self.render_to_response(
        self.get_context_data(
            form=form,
            pubspr_formset=pubspr_formset,
            pubpers_formset=pubpers_formset,
        )
    )
teconomix
  • 227
  • 1
  • 3
  • 14