Save m2m in FormView django - django

I'm trying to save a m2m field in a FormView.
Here is my code:
class ProductorPropietarioView(FormView):
form_class = FormPropietario
success_url = '/'
template_name = 'productores/propietario.html'
def form_valid(self,form):
form.save(commit=False)
form.save()
form.save_m2m()
return super(ProductorPropietarioView,self).form_valid(form)
models.py
class Persona(models.Model):
predio = models.ForeignKey(InfoPredioGeneral,related_name='predio+')
rol = models.ManyToManyField(RolPersona)
tipo_identificacion = models.ForeignKey(TipoIdentificacion,related_name='tipo identificacion+',blank=True,null=True)
numero_identificacion = models.CharField(max_length=100,blank=True,null=True)
forms.py
class FormPropietario(ModelForm):
class Meta():
model = Persona
fields = '__all__'
I can't get this to work. I know that first I have to set False then save the form and then save the m2m. I already tried only with form.save()
What am I doing wrong?

Try changing your FormView as follows:
def form_valid(self,form):
f = form.save(commit=False)
f.save()
form.save_m2m()
return super(ProductorPropietarioView,self).form_valid(form)

Related

Use Form.has_changed() validation with Django UpdateView

I am trying to post a message when one or more of the values have changed in an UpdateView. Is this possible to do with Form.has_changed() with this generic view? For example:
class MyUpdateView(UpdateView):
model = MyModel
form = MyModelForm
fields = "__all__"
template_name = "mymodel_form.html"
if form.has_changed():
logger.info("Some values have changed")
You can override the .form_valid(…) method [Django-doc] for this:
class MyUpdateView(UpdateView):
model = MyModel
form_class = MyModelForm
fields = '__all__'
template_name = 'mymodel_form.html'
def form_valid(self, form):
if form.has_changed():
logger.info('Some values have changed')
return super().form_valid(form)
or if you want to log changes if the form might be invalid as well, you can use .get_form(…) [Django-doc]:
class MyUpdateView(UpdateView):
model = MyModel
form_class = MyModelForm
fields = '__all__'
template_name = 'mymodel_form.html'
def get_form(self, form_class=None):
form = super().get_form(form_class=form_class)
if form.has_changed():
logger.info('Some values have changed')
return form

Send the logged user Profile Model to a CreateView form

what I am trying to accomplish is to send the "requester" model, using the logged-in user to a form ...
Mainly the problem that I have is that the views.py "class CreateOrderView(CreateView)" does not have a parameter "request" , so I cannot get the request.user, and therefore get requester_obj and automatically select this requester_obj in the form field "requester", when entering this page.
models.py Order:
DEFAULT_REQUESTER_ID= 1
requester = models.ForeignKey(Profile, on_delete=models.CASCADE, default=DEFAULT_REQUESTER_ID, verbose_name="usuario")
forms.py:
class OrderCreateForm(BaseForm, forms.ModelForm):
date = forms.DateField(label="Fecha" , widget=forms.DateInput(attrs={'type': 'date'}))
class Meta:
model = Order
fields = ['requester','title' , 'date', ]
views.py:
#method_decorator(staff_member_required, name='dispatch')
class CreateOrderView(CreateView):
template_name = 'form.html'
form_class = OrderCreateForm
model = Order
def get_success_url(self):
self.new_object.refresh_from_db()
return reverse('update_order', kwargs={'pk': self.new_object.id})
def form_valid(self, form):
object = form.save()
object.refresh_from_db()
self.new_object = object
return super().form_valid(form)
I get the requester like this:
#login_required
def create(request):
#return render(request, 'app_client/create.html')
if request.method == 'POST':
if request.POST['value'] and request.POST['products']:
logged_user = request.user
user_obj = get_object_or_404(User, username=logged_user)
requestor_obj = get_object_or_404(Profile, user=user_obj)
....
I just found a solution for my issue...
What I did was to remove the "requester" field in forms.py, and send the requester obj to the form after user presses the submit form button
def form_valid(self, form):
logged_user = self.request.user
user_obj = get_object_or_404(User, username=logged_user)
requester_obj = get_object_or_404(Profile, user=user_obj)
form.instance.requestor = requestor_obj
object = form.save()
object.refresh_from_db()
self.new_object = object
return super().form_valid(form)
form.instance.requestor = requestor_obj was the line that I needed to send it to form before saving it.
ref: Django CreateView Foreign key

Why i create two records at once using django-bootstrap-modal-forms

I'm using django-bootstrap-modal-forms.
My forms.py:
class UserAppForm(BSModalForm):
class Meta:
model = UserApp
fields = ('app', 'app_type')
In view, in order to attach current user, i override form_valid():
class AppCreateView(BSModalCreateView):
template_name = 'apps/app_create.html'
form_class = UserAppForm
success_message = 'Success: App was created.'
success_url = reverse_lazy('dashboard')
def form_valid(self, form):
app = form.save(commit=False)
profile = Profile.objects.get(user=self.request.user)
app.profile = profile
app.save()
return redirect(self.success_url)
But, if i try to create UserApp, i get two instances at once.
Where is my mistake?
Correct way to override form_valid() is:
def form_valid(self, form):
if not self.request.is_ajax():
app = form.save(commit=False)
profile = Profile.objects.get(user=self.request.user)
app.profile = profile
app.save()
return HttpResponseRedirect(self.success_url)

Why aren't the images of an inlineformset_factory being saved?

I have two models: Profile and CredentialImage.
I am trying to allow each Profile to upload, optionally, up to 5 maximum images(CredentialImage).
I've decided to use an inlineformset_factory for the images because on the UpdateView users will be given the option of updating their general Profile information as well as their 5 select images.
The code goes without error, but the images do not save to the database.
Here are the two models:
class Profile(models.Model):
...
def get_absolute_url(self):
return reverse("profile:profile_detail",
kwargs={"username": self.user})
class CredentialImage(models.Model):
profile = models.ForeignKey(Profile, default=None)
image = models.ImageField(upload_to=credential_photo_upload_loc)
The modelforms + initialization of the inlineformset_factory:
from django.forms.models import inlineformset_factory
class ProfileUpdateForm(ModelForm):
class Meta:
model = Profile
fields = [
"introduction",
"biography",
]
class CredentialImageForm(ModelForm):
image = ImageField()
class Meta:
model = CredentialImage
fields = ['image', ]
CredentialImageFormSet = inlineformset_factory(Profile,
CredentialImage, fields=('image', ), extra=4)
A class-based UpdateView for updating a Profile:
class ProfileUpdateView(LoginRequiredMixin, UpdateView):
form_class = ProfileUpdateForm
template_name = 'profile/profile_edit.html'
def get_context_data(self, **kwargs):
context = super(ProfileUpdateView, self).get_context_data(**kwargs)
if self.request.POST:
context['credential_image'] = CredentialImageFormSet(self.request.POST)
else:
context['credential_image'] = CredentialImageFormSet()
return context
def get_object(self, *args, **kwargs):
user_profile = self.kwargs.get('username')
obj = get_object_or_404(Profile, user__username=user_profile)
return obj
def form_valid(self, form):
data = self.get_context_data()
formset = data['credential_image']
if formset.is_valid():
self.object = form.save()
formset.instance = self.object
formset.save()
return redirect(self.object.get_absolute_url())
instance = form.save(commit=False)
instance.user = self.request.user
return super(ProfileUpdateView, self).form_valid(form)
I'm especially wary of the get_context_data and form_valid.
Is it correct to try and instantiate the formset using get_context_data and to save both within form_valid?
You need to pass request.FILES to the formset as well as request.POST when you are uploading files:
context['credential_image'] = CredentialImageFormSet(self.request.POST, self.request.FILES)
The get_context_data method is meant for getting the context for the data. You shouldn't be instantiating formsets there. You could have a look at the UpdateWithInlinesView from django-extra-views.

Multiple images in django form with multiupload

I need to add multiple images in django form to one model. I did a research and for form outside of django I try to setup django-multiupload.
My models.py:
class Profile(models.Model):
...
...
first = models.ImageField("first", upload_to='first')
second = models.ImageField("second", upload_to='second')
...
In forms.py:
class AddForm(forms.ModelForm):
first = MultiImageField(min_num=1, max_num=20)
second = MultiImageField(min_num=1, max_num=4)
In views.py:
class UploadView(FormView):
template_name = 'finder/submit.html'
form_class = AddForm
success_url = '/'
def form_valid(self, form):
for each in form.cleaned_data['first']:
Profile.objects.create(first=each)
for each in form.cleaned_data['second']:
Profile.objects.create(second=each)
return super(UploadView, self).form_valid(form)
And on submitting form this form creates multiple Profile objects with only first/second field filled.
How can I create only one model with remaining fields (other than first/second) and with multiple first/second fields?
It was my function-based view before adding multiupload but I couldn't make it work, maybe it's easier to change it somehow?
def add_form(request, *args, **kwargs):
if request.method == "POST":
form = AddForm(request.POST)
if form.is_valid():
profile = form.save(commit=False)
profile.save()
return redirect('/', pk=profile.pk)
else:
form = AddForm()
return render(request, 'finder/submit.html', {'form': form})
I have never used the Django-multiupload, but I happen to read some of the docs.
If you want to save multiple files for your user model, you may need to create another model for accommodating the files and add a Foreign Key towards the Profile model.
Remove the first and second fields from Profile model. It causes you to create multiple profiles with same data inorder to accomodate multiple images.
Simple example,
class Image(models.Model):
image = models.FileField()
profile = models.ForeignKey(Profile, related_name='images')
is_first = models.BooleanField(default=False)
is_second = models.BooleanField(default=False)
Then, edit the save method in form,
class AddForm(forms.ModelForm):
first = MultiImageField(min_num=1, max_num=20)
second = MultiImageField(min_num=1, max_num=4)
class Meta:
model = Profile
fields = (.......... 'first', 'second')
def save(self, commit=True):
first_images = self.cleaned_data.pop('first')
second_images = self.cleaned_data.pop('second')
instance = super(AddForm, self).save()
for each in first_images:
first = Image(image=each, profile=instance, is_first=True)
first.save()
for each in second_images:
second = Image(image=each, profile=instance, is_second=True)
second.save()
return instance
Then, on the views, edit the view,
class UploadView(FormView):
template_name = 'finder/submit.html'
form_class = AddForm
success_url = '/'
def form_valid(self, form):
instance = form.save(commit=False)
instance.user = self.request.user
instance.save()
return super(UploadView, self).form_valid(form)
Or in function based view,
def add_form(request, *args, **kwargs):
if request.method == "POST":
form = AddForm(request.POST)
if form.is_valid():
profile = form.save(commit=False)
profile.user = request.user
profile.save()
return redirect('/', pk=profile.pk)
else:
form = AddForm()
return render(request, 'finder/submit.html', {'form': form})