Django formset: Unable to save - django

Trying to setup simple formset, but getting errors
'SWDataForm' object has no attribute 'save'
MODEL
class SWData(ValidateOnSaveMixin, models.Model):
model_serial = models.ForeignKey(SWInfo, related_name='serial_items', on_delete=models.SET_NULL, null=True)
hostname = models.CharField(max_length=20, default='', unique=True)
deployed = models.BooleanField()
class Meta:
verbose_name_plural = "SWDATA"
def __str__(self):
return "{0}".format(self.hostname)
VIEW
def display_data(request, data, **kwargs):
return render(request, 'web/posted-data.html', dict(data=data, **kwargs))
def swdata(request, *args, **kwargs):
template = 'web/swdata.html'
SWDataFormset = modelformset_factory(SWData, fields=('__all__'), extra=1)
formset = SWDataFormset(request.POST or None)
if request.method == 'POST':
print(formset.is_valid())
if formset.is_valid():
pprint(formset)
for form in formset.forms:
if form.is_valid():
try:
if form.cleaned_data.get('DELETE') and form.instance.pk:
form.instance.delete()
else:
instance = form.save(commit=False)
#instance.model_serial = model_serial
#print (instance.model_serial)
instance.save()
messages.success(request, "Successfully")
except formset.DoesNotExist:
messages.error(request, "Database error. Please try again")
#data = form.cleaned_data
#return display_data(request, data)
else:
formset = SWDataFormset(request.POST or None)
return render(request, template, {'formset': formset})
Remove the form and used the modelformset_factory, I was able to save only the last entry in the formset. How to loop thru each formset prefix and save each item?

I think that you are trying to save data form from a form, and you can't do that. You need to use the method create of your model. Something like SWDData.objects.create(form).

remove this line
instance = form.save(commit=False)
this line is needed when you wants to edit the form.
Now check if it works
Even I also got similar Problem

Related

Django/Python: How to change filename when saving file using models.FileField?

I found this example to upload a file using FileField and it works great.
https://simpleisbetterthancomplex.com/tutorial/2016/08/01/how-to-upload-files-with-django.html
Problem is that it saves the original filename of the file being uploaded. I don't want that. I can change the filename within models.py by overriding the save function (see below). For the life of me, I cannot figure out how to pass a filename in when I execute form.save() from views.py. I need to know the filename for another process. I thought about even returning a filename from the models.py save function. I'm a bit of a noob so forgive any missing details. I've searched this site and read loads of documentation, but I'm missing something. Any advice would be appreciated.
Forms.py
class DocumentForm(forms.ModelForm):
message = forms.CharField(widget=forms.Textarea(attrs={'rows': 5, 'cols': 50}))
class Meta:
model = Document
fields = ('description', 'document', )
Models.py
class Document(models.Model):
description = models.CharField(max_length=255, blank=True)
document = models.FileField(upload_to='atlasapp/documents/')
uploaded_at = models.DateTimeField(auto_now_add=True)
def save(self, *args, **kwargs):
randomNum = random.randint(10000,90000)
new_name = str(randomNum) + ".txt"
self.document.name = new_name
super(Document, self).save(*args, **kwargs)
Views.py
def model_form_upload(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
form.save()
return redirect('model_form_upload')
else:
form = DocumentForm()
return render(request, 'model_form_upload.html', {'form': form})
Could you perhaps call save() on the form with commit=False, set the name on the Document file, and then save the Document? For example:
def model_form_upload(request):
if request.method == 'POST':
form = DocumentForm(request.POST, request.FILES)
if form.is_valid():
document = form.save(commit=False)
document.name = 'some_new_name'
document.save()
return redirect('model_form_upload')
else:
form = DocumentForm()
return render(request, 'model_form_upload.html', {'form': form})

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})

Queryset filtering on Foreignkey in Modelform

I have a strange issue when saving a model form. I have a form, which consists of two model forms and I am trying to save them at the same time. For clarity, below is my code
Views.py
def create_user(request):
if request.method == 'POST':
user_form = UserForm(request.POST)
my_user_form = MyUsersForm(request.POST)
if user_form.is_valid() and my_user_form.is_valid():
us = user_form.save()
my_us = my_user_form.save(commit=False)
my_us.user = us
my_us.save()
return HttpResponse('You have successfully created a user')
else:
return HttpResponse(' My_user_form is not validated')
else:
user_form = UserForm()
my_user_form = MyUsersForm(user=request.user)
return render(request, 'create_user.html', {'user_form': user_form, 'my_user_form': my_user_form})
my_user_form is not validated when I override the init method of MyUsersForm to filter the queryset of the foreign key(created_by) but when I don"t filter the queryset, my_user_form is validated and the form is saved.
What I don't understand is when I don"t filter the query set how come my_user_form is validated?
The data which is sent via the request.post to my_user_form is somehow lost (when I filter the queryset). any clue in the right direction is highly appreciated. Thank you for your valuable inputs.
Forms.py
class MyUsersForm(ModelForm):
class Meta:
model = MyUsers
fields = ['created_by', ]
def __init__(self, user=None, **kwargs):
super(MyUsersForm, self).__init__(**kwargs)
if user is not None:
self.fields['created_by'].queryset = User.objects.filter(username=user)
Models.py
class MyUsers(models.Model):
user = models.OneToOneField(User, blank=True, null=True)
created_by = models.ForeignKey(User, related_name="created_by", blank=True, null=True)
def create_user(request):
if request.method == 'POST':
user_form = UserForm(request.POST)
my_user_form = MyUsersForm(request.POST)
if user_form.is_valid() and my_user_form.is_valid() and User.objects.filter(username=request.POST.get('user')).exists():
us = user_form.save()
my_us = my_user_form.save(commit=False)
my_us.user = us
my_us.save()
return HttpResponse('You have successfully created a user')
else:
return HttpResponse(' My_user_form is not validated')
else:
user_form = UserForm()
my_user_form = MyUsersForm(user=request.user)
return render(request, 'create_user.html', {'user_form': user_form, 'my_user_form': my_user_form})
why my_user_form is not validated and saved. instead of modifying the queryset in the init i did it in the view itself using the statement
my_user_form.fields['created_by'] = forms.ModelChoiceField(User.objects.filter(username=request.user))
and this solves my problem. but i still don't know why it didn't work in the init method of the MyUsersForm?.

Issues Saving Django ModelForms with m2m Relations

I'm trying to use Django ModelForms to allow teachers to schedule weekly classes with students. However, whenever I save a new instance of the weekly class from the modelform the m2m relations (students and teachers) are not saved.
I've looked pretty extensively in the Django documentation and tried everything to remedy this including setting commit=False on the save method and then using the save_m2m method. No dice.
Here's my code
models.py
class WeeklyClass(models.Model):
status = models.CharField(
max_length=1,
choices=STATUS_CHOICES,
default="A")
students = models.ManyToManyField(
Profile,
limit_choices_to=models.Q(is_teacher=False),
related_name='student_weekly_classes',)
teachers = models.ManyToManyField(
Profile,
limit_choices_to=models.Q(is_teacher=True),
related_name='teacher_weekly_classes',)
class Meta:
verbose_name = 'Class'
verbose_name_plural = 'Classes'
ordering = ["-created"]
forms.py
class WeeklyClassForm(ModelForm):
class Meta:
model = WeeklyClass
fields = [
"status",
"students",
"teachers",
"weekday",
"duration_hours",
"hour",
"minute"]
views.py
#login_required
def new_weekly_class(request):
if request.method == "POST":
form = WeeklyClassForm(request.POST)
if form.is_valid():
form.save()
return redirect(reverse(
"weekly_class_list",
kwargs={"username": request.user.username}))
else:
form = WeeklyClassForm()
return render(
request,
"weekly_classes/new_weekly_class.html",
{"form": form})
form.save() does not directly save the many to many instances so after saving the form.save() also call form.save_m2m() to save manytomay relationship.
#login_required
def new_weekly_class(request):
if request.method == "POST":
form = WeeklyClassForm(request.POST, request.FILES)
if form.is_valid():
form.save()
form.save_m2m()
return redirect("weekly_class_list", kwargs={"username": request.user.username})
else:
form = WeeklyClassForm()
template = "weekly_classes/new_weekly_class.html"
context = {"form": form}
return render(request, template, context)
for reference: https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#the-save-method

Django - How to Save ImageField Even if Form is Not Valid

I have a fairly standard model and form. I have some mandatory fields with an ImageField. When the user choose an image and doesn't field the mandatory fields, the image isn't saved and the user needs to 're-upload' it again.
As the row in the database already exists (because this form is part of a 'wizard'), I don't mind saving the picture in the database even if all the form isn't valid with all the mandatory data.
This is what I have right now in my view, which works when you fill all the mandatory fields:
def my_view(request):
instance = MyModel.objects.get(user=request.user)
form = MyForm(instance=instance)
if request.POST:
form = MyForm(request.POST, request.FILES, instance=instance)
if form.is_valid():
new_instance = form.save(commit=False)
if request.FILES and request.FILES['photo']:
uploaded_photo = request.FILES['photo']
new_instance.photo = uploaded_photo
new_instance.save()
return HttpResponseRedirect(reverse('new_url'))
return render_to_response('current_template.html', locals(), context_instance=RequestContext(request))
Here's what I tried to save the picture in DB even if the other fields aren't filled, but I get the error Django Upload Error - Upload a valid image (either not an image or corrupted):
def my_view(request):
instance = MyModel.objects.get(user=request.user)
form = MyForm(instance=instance)
if request.POST:
form = MyForm(request.POST, request.FILES, instance=instance)
if request.FILES and request.FILES['photo']:
uploaded_photo = request.FILES['photo']
instance.photo = uploaded_photo
instance.save()
if form.is_valid():
new_instance = form.save()
return HttpResponseRedirect(reverse('new_url'))
return render_to_response('current_template.html', locals(), context_instance=RequestContext(request))
Here's my form (fairly simple):
class MyForm(ModelForm):
first_name = forms.CharField(label='First Name', max_length=50, required=True)
last_name = forms.CharField(label='Last Name', max_length=50, required=True)
photo = forms.ImageField(required=False)
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
class Meta:
model = MyModel
fields = ('first_name','last_name', 'photo')
Here's my model (again very simple):
class MyModel(models.Model):
first_name = models.TextField(max_length=50)
last_name = models.TextField(max_length=50)
photo = ImageField(upload_to=get_photo_path,null=True,blank=True)
This is how I made it work. Notice that there's no 'request.FILES' as a parameter for the constructor of the form in the 'else' when the form is not valid. This is what made Django display the error message.
if form.is_valid():
instance = form.save(commit=False)
if request.FILES and request.FILES['photo']:
instance = save_photo(instance, request.FILES['photo'])
instance.save()
return HttpResponseRedirect(reverse('url'))
else:
if request.FILES and request.FILES['photo']:
instance = save_photo(instance, request.FILES['photo'])
form = InstanceForm(request.POST, instance=instance)