OneToOneField() create and update

In my project I have models with ForeignKey() relationships, which I manage by a ListView with a CreateView, UpdateView and DeleteView, something like Django’s admin.

I also have OneToOneField fields, I don’t see how to use Django’s CreateView, UpdateView and DeleteView in this case, because on for hand I don’t know what the user wants to do.

Is there sort of a best practice to manage OneToOneField relationships?

Kind regards,

Johanna

That seems to be a pretty fundamental question needing to be answered.

Beyond that, there are at least two ways to handle them.

The key difference here is that unlike an FK relationship, there’s only going to be one target item. This means that ListViews don’t really apply.

The most common patterns I’ve seen are either:

  • Include the related model on the same page as the base model, possibly with some UI work to show the difference between the base model and the related models.

    • Note: This is “awkward” with the Django-provided generic CBVs, because those are built around the idea of “one-model-per-page”. You can make them work with multiple models on a page, but my opinion is that they’re not worth the effort - you end up doing more work than if you built your view essentially from scratch (either inheriting from View if you’re really tied to CBVs or building the appropriate FBV).

    • Note: It may be possible to also accomplish this using an inline formset limiting to 1 entry. I’ve never tried it, so I don’t know what issues may be encountered in doing so…

  • Provide links on the edit page of the base model to other edit pages for those related models.

    • As a variation of the above, you could build a “wizard”-style interface where saving each model takes you to the next page for the next model in the set. If you need more sophisticated handling, you could look at the Form wizard in the Django formtools package. (Note: I use formtools in a couple of projects, it works very well for us.)

Hi Ken,

Thanks for your reply.

I built a prototype in an other python web framework. I had a page with links, one of which was to add for instance a logo. The link referenced a form as described below. Which is what you describe in your second option.

In that framework I would query the database for a record, which I would provide to the form constructor together with the model. In case the record existed I got an update form in case the record didn’t exists I would get a create form.

Does Django have a mechanism as described above, so an update form which becomes a create form in case there is no record for the id.

Kind regards,

Johanna.

Not within the context of the system-provided generic ClassBasedViews.

You can do this yourself, either as a CBV or an FBV, but you would be writing the logic in the view to do this.

Note: I might approach this from a slightly different perspective. Assume the two classes are named Base and Related, where Related has the OneToOne relationship with Base.

There are two fundamentally different options here. If Base is being created, then by definition the form for Related must be a CreateView.

But if Base is being updated, then the link to edit Related could either be to a CreateView or an UpdateView.

If I were doing this using the Django generic CBVs, I might override the url for those links being generated in Base to check to see if Related already exists, and create the link accordingly. (In other words, when I’m rendering the template for Base, the link I create on that page would be different depending upon whether Related already exists.) Then I don’t need to worry about adding additional logic in the view. The view being referenced is the right one.

(Yes, the risk of an adverse race condition being caused by simultaneous edits is greater in this case than handling it as logic within the view, but if that’s such a serious consideration then I would suggest you need to handle it anyway regardless of the mechanism being used.)

Hi Ken,

Thanks for your reply. It took some time to get my head around your solution, but I learned a lot from exploring it.

Kind regards,

Johanna