Simple ForeignKey in ClassBasedView in django - django

I have a model with a foreignkey to another model
class Person(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
class Organisation(models.Model):
name = models.CharField(max_length=100)
address = models.CharField(max_length=100)
contact = models.ForeignKey(Person)
I want to use a CreateView to be able to create a new Organisation, but be able to enter a new contact person details on the same page (i.e. when a new organisation is created, a new contact person must also be created).
What is the nicest (DRY) way to do this?

In the CreateView use the model that has the ForeignKey and since it inherits the FormMixin's form_class use the modelform_factory for that model with extra fields the fields of ForeignKey's model. Finally, overload either the validation or save methods with a get_or_create with the ForeignKey's model fields, passing the result to the ModelForm.
An alternative approach would be to chain two CreateViews. First with the Organization as the model, using the Contact's CreateView URL as its success_url. You can even use js to replace the first submit with the html of the second view.
Or you can try some hacks floating around utilizing formsets, though I prefer the first two methods in your case. The formset hacks are better suited for many-to-many relationships.

Related

Limit Django ModelForm ForeignKey choices depending on request.user

It is sometimes desirable to restrict the choices presented by the ForeignKey field of a ModelForm so that users don't see each others' content. However this can be tricky because models do not have access to request.user.
Consider an app with two models:
class Folder(models.Model):
user = models.ForeignKey(get_user_model(), editable=False, on_delete=models.CASCADE)
name = models.CharField(max_length=30)
class Content(models.Model):
folder = models.ForeignKey(Folder, on_delete=models.CASCADE)
text = models.CharField(max_length=50)
The idea is that users can create folders and content, but may only store content in folders that they themselves created.
i.e.:
Content.folder.user == request.user
QUESTION: How can we use for example CreateView, so that when creating new content users are shown the choice of only their own folders?
I do this by overriding CreateView.get_form_class() in order to modify the attributes of the relevant field of the form before it is passed to the rest of the view.
The default method, inherited from views.generic.edit.ModelFormMixin, returns a ModelForm that represents all the editable fields of the model in the base_fields dictionary. So it's a good place to make any desired changes and also has access to self.request.user.
So for the above example, in views.py we might say:
class ContentCreateView(LoginRequiredMixin, CreateView):
model = Content
fields = '__all__'
def get_form_class(self):
modelform = super().get_form_class()
modelform.base_fields['folder'].limit_choices_to = {'user': self.request.user}
return modelform
Read more about ForeignKey.limit_choices_to in the docs.
Note that field choices are enforced by form validation so this should be quite robust.

django restframework: blank=False and default='x' creates a modelsearializer with required=False field. how to circumvent?

See example below, that will create a serializer with a not required testing field (obviously, as there is a default). I know I can add the field manually to the serializer, with required=True. But, having many fields, I would like to have DRF do the work for me. Or I could remove the default on the modelfield. Dont want this either...is there a thing, like required_fields, on the Meta, perhaps? Or another workaround?
Model
class MyModel(models.Model):
testing = models.CharField(max_length=3, blank=False, default='x')
Serializer
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
The reason why I need this: Having a vue.js CRUD like app, that is creating new instances...without specifying the initial fields on the object.

Add a unique "intermediary" model to all my Django ManyToManyField()

Hello Awesome People!
Such a question that I have made a lot of searches for it. I am done building a website two(2) months ago, but today The team decides to track every time an instance has been added to a Model in ManyToManyField() fields.
I was thinking using the through argument to point to the model that will act as an intermediary may work but not at all in my case (70%). Just Because I want to have a unique intermediary model that will record for all ManyToManyField()
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateTimeField(auto_now_add=True)
Ah! Something is required. I need to explicitly specify foreign keys to the models that are involved in the many-to-many relationship.
Django ContentType may anticipate for all my models, but it's not working, I wonder why? it also contains ForeignKey (the one required by an intermediary model).
Do I really need to edit all my ManyToManyField fields and create Model as much as ManytoManyField? is there a way to record date_joined without creating an intermediary model for each?
Are you perhaps looking for something like django admin's LogEntry model?
LogEntry contains the ContentType of the model instance that has changed, the id of the instance, the type of change and an abstract change message. With all of that you can retrace changes made to instances.
In django admin, the views take care of adding records to LogEntry via three methods log_change/addition/deletion: click.

django admin Model edit form - How to filter foreignkeys to only related models

I have an account model in django which has a foreignkey to Payment and a onetoone to Address.
In the Account section in the admin, I can edit a specific model and edit the fields payment and address via a select widget. However how can I filter the options so that it only shows related models. (ie not every address or payment from every user, only the ones from that user).
The RelatedOnlyFieldListFilter seems to only apply to the model list view. Is there a way to use this in the model edit view?
What you are probably looking for is call inlines. It allows you to edit related object directly in the parent model form which in this case would be the Account model. Here is an example of implementation:
class Account(models.Model):
name = models.CharField(max_length=100)
class Payment(models.Model):
account= models.ForeignKey(Account, on_delete=models.CASCADE)
amount= models.CharField(max_length=100)
class Adress(models.Model):
account= models.OneToOneField(Account, on_delete=models.CASCADE)
adress= models.CharField(max_length=100):
class AccountAdmin(admin.ModelAdmin):
inlines = [
Paymentinline,
Adressinline,
]
Note that this is not a complete implementation (you will need to construct both inlines), follow the documentation on inlines for further information but above are the basics to it.

Show models.ManyToManyField as inline, with the same form as models.ForeignKey inline

I have a model similar to the following (simplified):
models.py
class Sample(models.Model):
name=models.CharField(max_length=200)
class Action(models.Model):
samples=models.ManyToManyField(Sample)
title=models.CharField(max_length=200)
description=models.TextField()
Now, if Action.samples would have been a ForeignKey instead of a ManyToManyField, when I display Action as a TabularInline in Sample in the Django Admin, I would get a number of rows, each containing a nice form to edit or add another Action. However; when I display the above as an inline using the following:
class ActionInline(admin.TabularInline):
model=Action.samples.through
I get a select box listing all available actions, and not a nifty form to create a new Action.
My question is really: How do I display the ManyToMany relation as an inline with a form to input information as described?
In principle it should be possible since, from the Sample's point of view, the situation is identical in both cases; Each Sample has a list of Actions regardless if the relation is a ForeignKey or a ManyToManyRelation. Also; Through the Sample admin page, I never want to choose from existing Actions, only create new or edit old ones.
I see your point but think of a case where you might need to use custom through model (table). In that case the admin inline form would include the fields for that intermediate model since thats the model you asked the admin to create the form for.
e.g.
class Person(models.Model):
name = models.CharField(max_length=128)
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
The admin should display the form for the Memebership model cause thats the model the editable instance is related to.
In your case the through model contains only the 2 foreign keys (1 for the Action model and 1 for the Sample) ands thats why only the list of actions appear.
You could do what you are asking for if django admin supported nested inlines (there is an open ticket about that).