Reputation: 21
I'm using Django class based generic view. In my models.py I have a model called MyModel with many-to-many field called m2m. I have multiple groups of users they can edit the m2m field. Each group of users can only see and add their portion to the field - using get_form to set what they can see in the m2m field. The problem I'm having is that when one user enter his record it will delete the initial records in the m2m field. I need to somehow get the initial values from the m2m field save them and then add them to the new ones when the form is submitted. Here is my views.py:
class MyModelUpdate(UpdateView):
model = MyModel
fields = ['m2m']
def get_initial(self):
return initials
def get_form(self, form_class=None):
form = super(MyModelUpdate, self).get_form(form_class)
form.fields["m2m"].queryset = DiffModel.objects.filter(user = self.request.user)
return form
def form_valid(self, form):
form.instance.m2m.add( ??? add the initial values)
return super(MyModelUpdate, self).form_valid(form)
def get_success_url(self):
...
Upvotes: 1
Views: 3128
Reputation: 21
after few days of searching and coding I've found a solution.
views.py:
from itertools import chain
from .forms import MyForm,
def MyModelUpdate(request, pk):
template_name = 'mytemplate.html'
instance = MyModel.objects.get(pk = pk)
instance_m2m = instance.m2m.exclude(user=request.user)
if request.method == "GET":
form = MyForm(instance=instance, user=request.user)
return render(request, template_name, {'form':form})
else:
form = MyForm(request.POST or None, instance=instance, user=request.user)
if form.is_valid():
post = form.save(commit=False)
post.m2m = chain(form.cleaned_data['m2m'], instance_m2m)
post.save()
return redirect(...)
forms.py:
from django import forms
from .models import MyModel
class MyForm(forms.ModelForm):
class Meta:
model = MyModel
fields = ['m2m']
def __init__(self, *args, **kwargs):
current_user = kwargs.pop('user')
super(MyForm, self).__init__(*args, **kwargs)
self.fields['m2m'].queryset = self.fields['m2m'].queryset.filter(user=current_user)
Upvotes: 1
Reputation: 1186
I'm adding this answer to offer a simplified explanation of this kind of problem, and also because the OP switches from an UpdateView to a function based view in his solution, which might not be what some users are looking for.
If you are using UpdateView for a model that has a ManyToMany field, but you are not displaying it to the user because you just want this data to be left alone, after saving the form all the m2m values will be erased.
That's obviously because Django expects this field to be included in the form, and not including it is the same as just sending it empty, therefore, to tell Django to delete all ManyToMany relationships.
In that simple case, you don't need to define the form_valid and then retrieve the original values and so on, you just need to tell Django not to expect this field.
So, if that's you view:
class ProjectFormView(generic.UpdateView):
model = Project
form_class = ProjectForm
template_name = 'project.html'
In your form, exclude the m2m field:
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
fields = '__all__'
exclude = ['many_to_many_field']
Upvotes: 1