Formset populated by wrong data - Django 1.8 - django

Basic problem: I have a list of products, and when I go to edit a single product, the image upload formset is populated with all images in the folder, but it should only be populated by related images (see left hand side of picture below).
When I try to make any changes - to the product or to the images - it is unsuccessful, and the input boxes which were populated by unrelated images now display 'this field is required' (see right hand side of picture below)
Obviously, I need to successfully edit the product object, and I want this page to be populated only by images related to that product.
The View
def EditProduct(request, pk):
instance = get_object_or_404(Product, id=pk)
ImageFormSet = modelformset_factory(ProductImage,
form=ImageForm, extra=4)
if request.method == 'POST':
product_form = AddEditProductForm(request.POST, request.FILES, instance=instance)
formset = ImageFormSet(request.POST, request.FILES,
queryset=ProductImage.objects.none())
if product_form.is_valid() and formset.is_valid():
product = product_form.save()
images = formset.save(commit=False)
for image in images:
image.product = product
image.save()
return HttpResponseRedirect('/product/')
else:
print (product_form.errors, formset.errors)
else:
product_form = AddEditProductForm(instance=instance)
formset = ImageFormSet(queryset=ProductImage.objects.all()) # possible wrong queryset
return render(request, 'product/edit.html',
{'product_form': product_form, 'formset': formset},
context_instance=RequestContext(request))
So this code is somehow returning ALL of my uploaded images - including unrelated images - to the editing form for a single product - how do I make it so only those images which are related to the instance are displayed in the form?
On a related note - how do you access an individual image in the formset - for example if i wanted to remove one, what kind of modifications am I looking at?

This depends on your model.
formset = ImageFormSet(queryset=ProductImage.objects.all()) # possible wrong queryset
This is indeed the wrong queryset. You should do something like:
queryset=ProductImage.objects.filter(product_id = pk)
if you gave the product_image field a related_name than you could do
queryset=instance.related_product_images
https://docs.djangoproject.com/en/1.8/ref/models/fields/#django.db.models.ForeignKey.related_name

Related

Django update functionality

I've got a model, with two forms. When a calf is scanned in, it gets one set of information, then when it's shipped out, it's a separate form with different information. For example when scanned in, it takes DOB and arrival date, when scanned out we need ship out date, milk consumed during it's stay(2 quarts per day), where it is going, and any medication while at the depot.
Right now I have the two forms below:
Scan in form
scan out form
Now you can see when I try to update an entry I get an error that it already exists
Here is my view:
def scanoutcalf(request, id=None):
form = ScanOutForm(request.POST or None)
context = {
'form': form,
}
form = ScanOutForm(request.POST or None)
if form.is_valid():
form.save()
return render(request, 'calves/scan_out.html', context)
And my forms:
class ScanOutForm(forms.ModelForm):
class Meta:
model = Calf
fields = [
'eid', 'ship_out_date', 'program', 'destination', 'medical_history', 'milk_consumed'
]
widgets = {
'ship_out_date': forms.widgets.DateInput(
attrs={'type': 'date'}
)
}
I've googled around, but couldn't find an example of how to update without having to create a queryset or a URL with instance ID.
The idea here is that a user can just scan when calves are leaving, update with the information that is essential, and the form will check the EID, and update the DB if it exists, or create it if it doesn't. Any help would amazing
Query the instance before saving and pass into ScanOutForm as keyword argument instance.
Like so:
def scanoutcalf(request, id=None):
form = ScanOutForm(request.POST or None)
context = {
'form': form,
}
if request.method == 'POST':
try:
calf = Calf.objects.get(eid=request.POST['eid'])
except:
calf = None
if calf is not None:
form = ScanOutForm(request.POST, instance=calf)
if form.is_valid():
form.save()
return render(request, 'calves/scan_out.html', context)
Note: As you use unique identifier in the form this must be directly extracted from request.POST with validation.

Django initial value for MultiChoice Field ignored for ModelForm

this is my first post here and I am very new to Django but I just can't seem to find a solution for this problem... I've searched stackoverflow and google but nothing seems to work for me...
I have a wine-app and want to be able to add and remove wines from the user's stock. In the list of wines the user can choose a wine to add and the ID of this wine is passed in the POST data. Since the data is getting lost after the first time the view is rendered I saved the ID in a cookie, which is working, but the problem is when I work with ModelForm de user has to select the foreign key for the user and for the wine, which is bad, so I tried to make it hidden and set the Fk_user and Fk_wine after the user choose the number of bottles to be added but before validation. Here's the problem after google everyone suggested I should use the "initial" and pass that to the form, but this is clearly not working because if I make the fields visible in the form I can see that it is not preselected...
viewy.py:
def addStockView(request):
wineId = request.POST.get('addStock')
if 'addStock' in request.POST:
wine = get_object_or_404(Wine, idwine=int(wineId))
userId = request.user.id
user = get_object_or_404(AuthUser, id=userId)
if request.method == 'POST':
#wineIdNew = request.COOKIES.get('wineIdToAdd')
#wineNew = get_object_or_404(Wine, idwine=wineIdNew)
form = StockForm(request.POST, initial={'fk_wine': wineNew.idwine, 'fk_auth_user': user.id})
if form.is_valid():
form.save()
return redirect('home')
else:
form = StockForm(initial={'fk_wine': wine.id,
'fk_auth_user': user.id})
response = render(request, 'addToStock.html', {'form': form})
response.set_cookie('wineIdToAdd', wineId)
return response
forms.py:
class StockForm(forms.ModelForm):
#fk_wine = ModelChoiceField(queryset=Wine.objects.all(),
# widget=HiddenInput())
#fk_auth_user = ModelChoiceField(queryset=AuthUser.objects.all(),
# widget=HiddenInput())
class Meta:
model = UserWineStock
fields = ['fk_auth_user', 'fk_wine', 'number']
can anyone help me with this..?
Yes, initial data is ignored when a form is bound to submitted data.
Instead of using initial here, you should exclude those two fields from the form and set them on the created object:
form = StockForm(request.POST)
if form.is_valid():
item = form.save(commit=False)
item.fk_wine = wine
item.fk_auth_user = request.user
item.save()
return redirect('home')
(Also, please don't call your fields things like fk_auth_user. Just call it user.)

Uploading images from two formsets - Django

I have a page where you can add a product, add a thumbnail, and add images for the product.
I am using to formsets, seen below. The problem is that images uploaded to the image formset are uploaded correctly, but images uploaded to the thumbnail formset are not uploading at all... what might I be doing wrong?
def AddProduct(request):
ImageFormSet = modelformset_factory(ProductImage,
form=ImageForm, extra=4)
ThumbnailFormSet = modelformset_factory(ProductvThumbnail,
form=ThumbnailForm)
if request.method == 'POST':
product_form = AddEditProductForm(request.POST)
image_formset = ImageFormSet(request.POST, request.FILES, prefix='images',
queryset=roductImage.objects.none())
thumbnail_formset = ThumbnailFormSet(request.POST, request.FILES, prefix='thumbnail',
queryset=ProductThumbnail.objects.none())
if product_form.is_valid() and image_formset.is_valid() and thumbnail_formset.is_valid():
product = product_form.save()
thumbnails = thumbnail_formset.save(commit=False)
for thumbnail in thumbnails:
thumbnail.product = product
thumbnail.save()
images = image_formset.save(commit=False)
for image in images:
image.product = product
image.save()
return HttpResponseRedirect('/product/')
else:
print (product_form.errors, image_formset.errors, thumbnail_formset.errors)
else:
product_form = AddEditProductForm()
image_formset = ImageFormSet(queryset=ProductImage.objects.none(), prefix='images')
thumbnail_formset = ThumbnailFormSet(queryset=ProductThumbnail.objects.none(), prefix='thumbnail')
return render(request, 'product/add.html',
{'product_form': product_form, 'image_formset': image_formset,
'thumbnail_formset': thumbnail_formset},
context_instance=RequestContext(request))
I have found a solution - my formsets seem to be fine, the issue was in my models.py. The ImageField in the ProductThumbnail model was called 'image = models.ImageField...' but it should have been called thumbnail - that is why no thumbnails were being uploaded - my for loop was not finding anything.

form instance of a model gives id=None Django

I'm misunderstanding something! If my model is not saved, it does not have an id associated with it. So if I have something like this:
views.py (inserting or editing existing info uses the same modelform)
def insert_or_modify(request, id=None):
if id is not None:
book = BookModel.objects.get(pk=id)
else:
book = BookModel()
if request.method == 'POST':
form = BookInfoForm(request.POST, request.FILES, instance=book)
if form.is_valid():
form.save()
....
return render_to_response(...)
I also have an image and use upload_to for the imagefield. There are two problems: id is None and I'm not sure how to manipulate/save the instance=book so that I would actually get an id. The second problem is the location I save my data to is way off. Even though the template tag book.img.url has the desired location of the book at http:127.0.0.1:8000/folder1/media/id/, the actual location is somewhere else:
Where I want to save my image to:
/project/folder1/media/id/
where id is the book id.
What I actually get:
/project/id/
(But 'id' becomes 'None' since it doesn't exist!)
My previous code worked. It would save to the correct location, but with this current code, it doesn't work. So the saving issue doesn't seem like it's due to settings.py since it worked previously.
EDIT: removed non-code from code formatting area
EDIT: I found out why I wasn't saving to the correct location. As it turned out, I forgot to uncomment something when I last modified settings.py. Saving to the location works now! Sorry guys!
EDIT: I think the id=None problem is caused by form.save(). If I avoid doing that and just save to the model directly, I don't have this problem.
Id assigns only on saving objects when you use autoincrement id field (default).
You can save item before handling image, and then save image.
May be you can not worry about image name - becouse django file storages dont have troubles with same image names. So if you just save file "image.png", and then save another file with name "image.png" - then it will be saved as "image_1.png"
def add_or_create(request, item_id=None):
item = get_object_or_404(BookModel, id=item_id) if item_id else None
form = BookInfoForm(request.POST or None, request.FILES or None, instance=book) # assume it is ModelForm
if form.is_valid():
book = form.save()
For the first part:
def insert_or_modify(request, id=None):
if id:
book = BookModel.objects.get(pk=id)
if request.method == 'POST':
form = BookInfoForm(request.POST, request.FILES, instance=book)
if form.is_valid():
save_book = form.save()
# use save_book as your instance of BookModel
....
else:
if request.method == 'POST':
form = BookInfoForm(request.POST, request.FILES)
if form.is_valid():
save_book = form.save()
# use save_book as your instance of BookModel
....
save_book = form.save() allows you to then use save_book as your saved instance of BookModel, and save_book.id is its id.
def create_id(instance,some_id=None):
if some_id is None:
obj=Post.objects.first()
new_id=obj.id
new_id+=1
return new_id
else:
return some_id
def pre_save_post_receiver(sender, instance, *args, **kwargs):
if not instance.id:
instance.id = create_id(instance)
pre_save.connect(pre_save_post_receiver, sender=Post)

Django - Adding initial value to a formset

I have a many-to-many relationship between two classes (Lesson and Student), with an intermediary class (Evaluation).
I am trying to set up a form which will allow me to add a lesson with students and the related evaluation data. I can get all of the fields I want to display correctly, however I also need to set an initial value behind the scenes (the current user), as it does not make sense to have it in the form.
I have tried following the docs but I think I have a syntax error in the way I am passing the data to the formset.
The error I receive is as follows:
__init__() got an unexpected keyword argument 'initial'
My actual view (with my attempt at adding the initial data removed) looks like this:
def addlesson(request):
LessonFormset = inlineformset_factory(Lesson, Evaluation, exclude=('user',), max_num=5)
if request.method == 'POST':
lesson = Lesson(user=request.user)
form = LessonForm(request.POST, instance=lesson, user = request.user)
formset = LessonFormset(request.POST, instance = lesson)
if form.is_valid() and formset.is_valid():
form.save()
formset.save()
return HttpResponseRedirect("/")
else:
form = LessonForm(user = request.user)
formset = LessonFormset()
return render_to_response("addlesson.html", {
'form': form,
'formset' : formset,
}, context_instance=RequestContext(request))
Could anyone show me to correct syntax to use to set the current user in the formset?
This is what I had before but it was giving me the error at the start of my post:
initial={'user': request.user},
Any advice appreciated
Thanks
It's not clear to me why you are using a formset when it looks like you only want to add one row. A regular form would have been how I would do it if there was only one row. But, here's how I set the default value in a formset.
I exclude the field, just like you already have in your code. Then:
if form.is_valid() and formset.is_valid():
form.save()
models = formset.save(commit=False)
for i in models:
i.user = request.user
i.save()
return HttpResponseRedirect("/")
I tried Umang's answer and it didn't work good for when you want to change a value with a specific index. When you save the formset it will change the values that was changed.
But if you change models = formset.save(commit=False) to models = formset
and then you also need to change i.user = request.user to i.instance.user = request.user
if form.is_valid() and formset.is_valid():
form.save()
# changed to formset instead of formset.save(commit=False)
models = formset
for model in models:
# changed to i.instance.user instead of i.user, except renamed i to model.
model.instance.user = request.user
model.save()
# save the formset
formset.save()
return HttpResponseRedirect("/")
Now when you want to change an index it will include all the forms, not only the ones that was changed when you save.
Example:
views.py
if form.is_valid() and formset.is_valid():
form.save()
models = formset
index = 0
# This will only change the first form in the formset.
models[index].instance.user = request.user
models.save()
formset.save()
return HttpResponseRedirect("/")