How to submit a form to update an existing object - django

I have a model named Book that I send to an html to display data from like this:
books = Book.objects.all()
return render(request, 'index.html', {'books':books})
I then show all the information I want of the book in the front-end (which doesn't need to be shown here).
Other than showing the information of the book in the front-end I also want to introduce a small form that will have two buttons let's say "Submit" and "Decline" that will update an attribute of the Book object depending on the button clicked.
I am trying to find a good way to achieve this. My thought is to POST the entire object to a view to process later but I am not sure if is a good practice to submit an entire object from client to server as this may be affected in some way.

This may help you :
If you want to update database with other values related with Book's object then you can achieve it by doing the following:
if request.POST:
book_form = BookForm(request.POST)
if book_form.is_valid():
book = Book.objects.get(pk=book_id)
book_form = BookForm(request.POST, instance = book)
book_form.save()
It will update the existing bookObject in Database by filtering on the basis of book_id.

You don't need to send any form data if it's just an attribute you'll update. You can do something like this (I'm using uuid just as an example you can use id or whatever unique field you want):
# url for your update view. For example: decline
url(r'^decline/book/(?P<book_uuid>[\w\-]+)$',
YourView.as_view(),
name="book-decline"),
# views.py
class YourView(View):
def get(self, request, *args, **kwargs):
book_uuid = kwargs.get('book_uuid', None)
book = get_object_or_404(Book, uuid= book_uuid)
# UPDATE YOUR BOOK HERE
# in your templates for each book's decline button link
<a href={% "book-decline" book.uuid %} ....>

Tiny Instance provided a generic solution. If you want a more specific solution for the case of updating an object of a model by getting a form filled from the user, then Django has something inbuilt which does this for you. FormView is what you are looking for.
See the official documentation here -> https://docs.djangoproject.com/en/1.9/ref/class-based-views/generic-editing/#formview

Related

Django - Transfer data from view to form

I am struggling with the update of database information with the forms, and simply passing information between views. I could really use some advice because I am fairly new to Django.
The flow goes like this:
1. First form; I transfer the article price and title to the view "event"
2. The view "event" handles title and price and ask for confirmation in the html form
3. Once confirmed, it directs that information to the view "transact_test", I want this view to handle the update of the database via a new form that is build with the Article model. But it provides the error message : "didn't return an HttpResponse object. It returned None instead."
To fix your error: In transact_test you are just calling render in the request.method == 'POST' block:
render(request, ...)
You need to return render:
return render(request, ...)
You should really take a look at some additional django tutorials you are making this harder than you need to. You should almost never manually render a form when using django. And as Tariq said, please don't use images.

Sending form to another view django

I am building a website and I want various views that will ask the user to request a quote from our page. I want to keep the code as DRY as possible so I am writing a view quote which will receive the quote requests from various views and, if there is a validation error redirect back to the page that made the request. I managed to solve this using the super bad practice 'global variables'. I need a better solution, I would like redirecting to respective view with the current form so I can iterate through the form.errors. Here is my code:
def send_quote(request):
form = Quote(request.POST)
if form.is_valid():
# do stuff when valid
return redirect('Support:thanks', name=name or None)
quote_for = request.POST['for_what']
global session_form
session_form = form
return redirect('Main:' + quote_for) # Here I would like to send form instead of storing in global variable`
You can use the HttpResponseRedirect function, and pass as argument the page that made the request.
return HttpResponseRedirect(request.META.get('HTTP_REFERER'))
All the META data is store on a dictionary, if you want to learn more check the documentation.
https://docs.djangoproject.com/en/dev/ref/request-response/#django.http.HttpRequest.META
If you redirect to the referrer, form.errors will be empty, as redirection is always a GET request.
I can think of two solutions to your problem:
Submit forms asynchronously using JavaScript and so populate the errors
Make all the views containing the form support POST - one way to do this would be to create a base class that inherits from FormView
The second option is a typical way of handling forms in Django - you process both POST and GET inside the same view.
After two days of searching I finally found the answer. Instead of saving form in request.session I just save request.POST and then redirect. Here is the code:
def send_quote(request):
form = Quote(request.POST)
if form.is_valid():
# do stuff when valid
return redirect('Support:thanks', name=name or None)
quote_for = request.POST['for_what']
request.session['invalid_form'] = request.POST
return redirect('Main:endview')
def endview(request):
session_form = request.session.pop('invalid_form', False)
if session_form:
form = Quote(session_form)
# render template again with invalid form ;)
Now I can repeat this with all the views I want and just change the what_for input of each form to match the respective view (Like I intended).

Django web application Taking user input and processing it

I am building a web application using django. I need to take a string input from the user and process it using a method I have written myself. How to achieve this in Django? Here are the things I need to do
Get User Input userinput = (string) On start page
Put this string as an argument in my method MyMethod(userinput) and Run it in the backend
Display what MyMethod() returns on another page.
I suggest that you start from django tutorial: https://docs.djangoproject.com/en/1.9/intro/tutorial01/
Basically, what you are going to need is form with one text field, HTML template that will render the form, view that will render HTML template with instance of a form when GET request arrives and call your MyMethod with value from form when POST request arrives and URL rule to call your view function on some URL.
Without additional data or any attempt to solve it and concrete problem you encounter - I can hardly offer more help.
You need to create a model with fields which you want to update by user input, then create a form based on this model. Then import this in a view and render it in a template
simple example:
forms.py:
class InputForm(forms.ModelForm):
class Meta:
model = YourModel
fields = ['fields_from_YourModel']
views.py:
from .forms import InputForm
def user_input(request):
input = CustomUser.objects.get(pk=request.user.pk)
if request.POST:
form = ProfileForm(request.POST, instance=input)
if form.is_valid:
form.save()
else:
form = ProfileForm()
return render(request, 'input.html', {'form':form})
Other steps more easier for beginner, you'll find examples in docs

Multiple Comment Forms on One Page in One Template - Django

I have researched this and many answers out there touch on it (formsets, prefixes, etc), but none is exactly what I'm after...
I have an application that allows users to post cards to a board and each card can have zero or more comments. I have the database relationships set up so if I manually add board, card, or comment objects I can display them in the template just fine.
The card form is also working, so this is not an issue. But now I want to display a comment textarea with a click event (which I have working with jQuery). The problem is, the form is not displaying the field. Here is the form code:
class AddCommentForm(ModelForm):
class Meta:
model = Comment
fields = ['message']
message = forms.CharField(
widget=forms.Textarea(
attrs={
'class': 'form-control comment-input'
'name': 'message',
'placeholder': 'Comment away...'
}
),
required=False,
)
Right off the bat, there is a problem in that when Django renders a form, it uses an id. In this case, it ends up being id_message or message (depending on whether I specify in form class). Of course, in HTML, this is no good. I confirmed this in the shell:
>>> import django; import project
>>> from apps.board.forms import AddCommentForm
>>> comment_form = AddCommentForm(); print(comment_form)
<tr><th><label for="message">Message:</label></th><td><textarea class="form-control comment-input" cols="40" id="message" name="message" placeholder="Comment away..." rows="10">
</textarea></td></tr>
So I thought of using formsets but it seems not quite right, because as I understand them, they are for several forms inside one form tag. My case is different, in that each card has zero or more comments and the flow of the HTML dictates that there should be several form elements.
Another challenge is that I need to have two views: one to process the cards and one to process each comment. I'm currently using different action values to accomplish this and it seems to work.
It seems like my use case is not so strange, and I can think of several cases where this kind of situation would occur: Twitter replies, comments on blog posts, etc. I just can't get Django to work with this.
I should also note that I can manually make this work with this:
<label>
<textarea class="form-control comment-input" name="message"></textarea>
<button type="submit"></button>
</label>
UPDATE
Here is a view segment:
class AddCommentView(View):
model = Comment
form_class = AddCommentForm
template_name = 'somedir/template.html'
#method_decorator(login_required)
def get(self, request):
comment_form = self.form_class()
return render(request, self.template_name, {
'comment_form': comment_form
})
If the form is working (from snippet above and shell test), I would expect this to show it on the page.
The URLConf is here:
url(r'^comment/add/$', AddCommentView.as_view(), name='add_comment_view'),
In writing this out, I see a confusing piece. My URL for the view is /comment/add/ but I want the form to be rendered at /dashboard/. The problem is that there is another view at that URL already.
If I build the form without Django forms, then post data to /comment/add/ the comment is created and saved in the database. So I guess I'm still trying to figure out how to use two views in one template with multiple forms. #Daniel Roseman points out the use of prefix and I guess this is worth exploring more.
UPDATE 2
So I got this to work using prefixes by removing the URL for that view and combining the views into one view. Two forms, one view, one template. But...
The source still shows replicate ids. I ended up not putting the id in the form attribute list. Since this is invalid HTML, what is best way to get around this?
In my view:
card_model = Card
comment_model = Comment
card_form_class = AddCardForm
comment_form_class = AddCommentForm
. . .
#method_decorator(login_required)
def get(self, request):
card_form = self.card_form_class(prefix='card')
comment_form = self.comment_form_class(prefix='comment')
. . .
UPDATE 3
I have found a solution to this problem. To instantiate the form, you need to add auto_id=False to prevent multiple, replicate ids:
comment_form = self.comment_form_class(prefix='comment', auto_id=False)
You can use the prefix parameter when instantiating your forms to give them non-conflicting ids.
While using the Django form prefix was partially correct (thanks # Daniel Roseman), the assertion that it doesn't matter how many form elements I have is not quite correct. It does matter, because by default the prefix was meant to be used in one form (as it creates multiple replicate ids). To use it in multiple forms, you also need auto_id=False. This prevents the id from being generated by the Django form.

Django Overwrite form data saved

I've posted about this problem before, but I still haven't found a solution so I'm hoping I'll have better luck this time.
I have a form that takes inputted data by the user. In another page, I am creating the identical form that the user has populated (pre-filled with that information) for editing purposes. Users will come to this page to EDIT the information they have already put in. My problem is that it isn't overwriting the instance.
def edit(request):
a = request.session.get('a', None)
if a is None:
raise Http404('a was not found')
if request.method == 'POST':
form = Name_Form(request.POST, instance=a)
if form.is_valid():
j = form.save( commit=False )
j.save()
else:
form = Name_Form( instance = a )
For this form, I'm using "unique_together" for some of the values. I'm also calling on `{{ form.non_field_errors }} in the template.
What is happening is when I make changes in the editing view, if the fields changes involves those defined in "unique_together" then an error is returned telling me that the instance already exists. Otherwise it saves a new instance. It isn't OVERWRITING.
Note that the reason i am using unique_together is that I want to prevent users from initially inputting the same form twice (before the editing stage, in the initial inputting view).
Any ideas?
EDIT: note that "a" refers to a session that includes a drop down box of all the available instances. This carried forward will indicate which instance the user wants to edit.
`
Why not do a database lookup of the model your trying to save and pull the fields from the form to the model then save the model?
Instead to store model a in session you should store it on database. Then edit it:
def edit(request, pk):
a = A.objects.get( pk = pk)
...
pk it the a identifier, you can send it to view via urls.py. I encourage to you to use POST/Redirect/GET pattern.
You can add a 'state' field on your model to control workflow (draft, valid)
You should not save objects in the session. If you really need to use a session - save a PK there and retrieve object right before giving it to Form. But the better solution is to send it in GET or POST parameters or included in url. Sessions are unreliable, data inside it can be destroyed between user's requests.
And you can retrieve value from a session in a more pythonic way:
try:
a = request.session['a']
except KeyError:
raise Http404('a was not found')