Django How to add a model other than a form - python

I am making an app using Django2.0. But There are things that I do not know even if I investigate.
There are models as follows.
class Picture(models.Model):
place = models.ForeignKey(Place, on_delete=models.CASCADE)
title = models.CharField(max_length=255)
image = models.ImageField(upload_to='image/')
using the following form
class PictureForm(forms.ModelForm):
class Meta:
model = Picture
fields = ['place', 'title', 'image']
And run following code on views.py
obj = Picture()
pic = PictureForm(request.POST, request.FILES, instance=obj)
pic.save()
I can add model peacefully. But in this way, I should select place and title on form every time. Beforehand I decide place and title, I will only select image on the form. It reduces my work.
Since there are several place and title, I do not want to set them to default values.
I succeeded in making a form.
class PictureForm(forms.ModelForm):
class Meta:
model = Picture
fields = ['image',]
But I don't know how to add title and place on views.py
I'm not good at English. Thank you for seeing the bad sentensecs to the end

Your issue comes from the view code, where you instantiate your new model.
If you want to create a new model you don't need to pass the insance argument, just edit the lines to get the following :
pic = PictureForm(request.POST, request.FILES)
pic.save()
See https://docs.djangoproject.com/en/2.0/topics/forms/modelforms/#the-save-method
To be able to set only a few fields you should update your model declaration, with for example null=True, Blank=True, default=None in the optional fields arguments.
PS : the django documentation is transalated in many languages, but if you don't find yours try the IRC channels.

If I understand your problem clearly,
You have predecided the place and the title and want the user to enter only the image. Only image should be kept in the form, but you want to enter the title and the place in the view.If that's the case,do this :
Your view function:
def some_view(request):
if request.method == "POST":
T,P = get_title_and_place()
# This fuction will return a tuple with the title and the place that you need.
# Or Alternatively, you can pass the title and place as argument to the view
obj = Picture(title=T,place=P)
#Initialise object with title and place received from function or arguments
pic_form = PictureForm(request.POST, request.FILES)
#The form contains only the image field as in your last block of code
if pic_form.is_valid():
obj.image = request.FILES['image']
obj.save()
#Save the object
return redirect('some_url')
else:
return render('xyz.html',{"form":pic_form})
#Incoming image is invalid. Throw back the invalid form to the user
else:
#Incoming method is not POST, give user the form to fill the image
pic_form = PictureForm()
return render('xyz.html',{"form:pic_form})
On submission of the form from xyz.html, you should redirect back to this view. This is the reason why I made a seperate function to get the title and place so that user doesn't know about it via the url.
However be careful with this method. You have to take care of validating the title and place. Otherwise the obj.save() will throw error

Related

Deleting FlaskForm Fields depending on userinput in other forms

I have 3 forms with Checkboxes to configure the desired form (Final_Form). After the user chooses the desired fields (in form1, form2 and form3), i want to delet all fields that are not required in the final form and render the final form. The reason for that is, that i have 3 Subkategories with around 12 possible values, in each form (form1-form3) the user can choose one ore more subkategories. The subcategories are standardized and are used to describe a clinical incident. The users wished to have the subcategories (1-3; form1-form3) seperated and always with an example (right-side of the screen in an anther bootstrap col).
The finalform is than a combination of the the subcategories that matches best to describe the clinical incident. All fields in the Final_Form are TextAreaFields. The Input for the TextAreaFields is stored in a sqlite-db.
Here is how i tried it:
app.py:
if request.method == 'POST' and form1.form1Submit.data:
OnePointOne = form1.OnePointOne.data
if not OnePointOne:
del Final_Form.OnePointOne
return render_template('Form2.html', form2 = form2)
if request.method == 'POST' and form2.form2Submit.data:
TwoPointTwo = form2.TwoPointTwo.data
if not TwoPointTwo:
del Final_Form.TwoPointTwo
return render_template('Form3.html', form3 = form3)
if request.method == 'POST' and form3.form3Submit.data:
ThreePointThree = form3.ThreePointThree.data
if not ThreePointThree:
del Final_Form.ThreePointThree
return render_template('Final.html', Final_Form = Final_Form)
forms.py:
class form1(FlaskForm):
OnePointOne = BooleanField('Effect')
form1Submit = SubmitField('Submit Category')
class form2(FlaskForm):
TwoPointTwo = BooleanField('Measure')
form2Submit = SubmitField('Submit Category')
class form3(FlaskForm):
ThreePointThree = BooleanField('Result')
form3Submit = SubmitField('Submit Category')
class Final_Form(FlaskForm):
OnePointOne = TextAreaField('Example Effect')
TwoPointTwo = TextAreaField('Example Measure')
ThreePointThree = TextAreaField('Example Result')
Final_FormSubmit = SubmitField('Submit incident')
The problem is, that the formfields of the Final_Form objects dont get deleted (only inside the if statements). I am very thankful for every hint or explanation.
As you are showing three separate pages, there are three separate requests.
You Final_Form object cannot be simply kept between these requests.
I don't fully understand why you configure your third form this way, it would be helpful to explain your use-case for better advice.
Without more information, I'm thinking of some ways to do this:
You make it one page/request, where you go from form to form using AJAX.
You make it one page with all forms, controlling visualisation with JS + CSS
You save your desired value somewhere
probably you can keep it in cookie (session object)
or in database, if that makes sense in your context
Also, please include whole code of this function - it's not clear how you create those forms you use.

How to use initial in a form with multiple objects without getting MultipleObjectsReturned Error

Why is form throwing me an exception of MultipleObjectsReturned, when trying to let users edit their profile cover, I have a model for account and i also created a separate model to hold the upload of users profile cover images given that users can have multiple upload of a profile cover images. but somehow i happen to get this error (get() returned more than one AccountCover -- it returned 2!) when upload is more than one cover image.
cover = get_object_or_404(AccountCover, account=account.id).first()
if request.user:
forms = CoverImageForm(request.POST, request.FILES,instance=cover,
initial = {
'cover_image':cover.cover_image.url,
})
if forms.is_valid():
data = forms.save(commit=False)
data.account = cover.account
data.save()
else:
forms = CoverImageForm(
initial = {
'cover_image':cover.cover_image,
}
)
Here is my model for cover image .
class AccountCover(models.Model):
account = models.ForeignKey(Account,on_delete=models.CASCADE)
cover_image = models.ImageField(upload_to=get_cover_image_path,blank=True, null=True)
Form for the coverimage
class CoverImageForm(forms.ModelForm):
class Meta:
model = AccountCover
fields = ['cover_image']
I think that get_object_or_404 needs to return only one object. Try instead with:
from django.http import Http404
cover = AccountCover.objects.filter(account=account.id).first()
if not cover:
raise Http404()

several forms on the same page with django

i have a very simple model... something like this:
class MachineTransTable(models.Model):
...
file = models.ForeignKey('File', related_name='the_file')
source = models.TextField()
target = models.TextField()
...
What I'd like to do is to have a page where the user has the source on the left (disable), the target on the right (editable) and a submit button to post the edited target text for EACH selected object in the MachineTransTable table. Here are some more information to better understand my request:
A page refers to a single file and there are several (sometimes hundreds) of objects in the MachineTransTable table belonging to the same file
Each time the user edit a single target and hit the submit button for that object, the object is saved/updated (depending on the initial value of the object) in the DB and the the user can continue to edit all the other objects...
At the end of the page there is another submit button which is used to exit from the page at the end of the work (all the objects have been edited/updated). If an object has not been edited/updated, it keeps its original value.
I tried to use a formset but I guess it's not the right choice... This is the file forms.py
class SegmentForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(SegmentForm, self).__init__(*args, **kwargs)
if self.instance.id:
self.fields['source'].widget.attrs['readonly'] = True
class Meta:
model = MachineTransTable
fields = ['source','target',]
SegmentFormSet = inlineformset_factory(File, MachineTransTable, form=SegmentForm, fields=('source','target'), extra=0)
and the view.py file is:
class CatUpdateView(LoginRequiredMixin,UpdateView):
Model = MachineTransTable
context_object_name = 'file'
template_name = 'app_cat/cat.html'
form_class = SegmentForm
...
def get_context_data(self, **kwargs):
context = super(CatUpdateView, self).get_context_data(**kwargs)
formset = SegmentFormSet(instance=self.get_object())
context['formset_Segment'] = formset
return context
Using this approach, I have a single form and all the related objects are saved/updated at once when the submit button is hit...
What can I do to achieve what I described above? Thanks
I think your choice is correct, you should not use several (hundreds eventually) forms here if the field in the same model. For two reasons:
You have to do a lot of repeat job to write so many forms and that is easy to make mistake and hard to maintain.
You still have to connect the database and update the record no matter how many field had been edit, and they almost efficiency.
But if you really want to do this,you can just use Ajax to post the current parameters name to the api, then update it, For instance, you have a button for target field:
value_in_the_html
Use Ajax to post the field name and value:
$ajax({
url: "api/table_id",
type: "POST",
datatype: "json",
data: {"field": "target", "value": "value_in_the_html"}
});
In view.py:
def UpdateTable(request, id):
field = request.POST.get("field", None)
value = request.POST.get("value", None)
machine = MachineTransTable.objects.filter(id=id).first()
machine.getattr(machine,field) = value
machine.save()
return HttpResponse("Saved!")

using a ModelForm to sanitize post data in Django

I've come across How to create object from QueryDict in django? , which answers what I want to do. However I want to sanitize the data. What does the Brandon mean by "using a ModelForm" to sanitize posted data?
ModelForm are very helpful when you want to create just model instances. If you create a form that closely looks like a model then you should go for a model form instead. Here is an example.
Going by the example provided in the Django website.
In your forms.py
class ArticleForm(ModelForm):
class Meta:
model = Articels #You need to mention the model name for which you want to create the form
fields = ['content', 'headline'] #Fields you want your form to display
So in the form itself you can sanitize your data as well. There are 2 ways of doing that.
Way 1: Using the clean function provided by Django using which you can sanitize all your fields in one function.
class ArticleForm(ModelForm):
class Meta:
model = Articels #You need to mention the model name for which you want to create the form
fields = ['content', 'headline'] #Fields you want your form to display
def clean(self):
# Put your logic here to clean data
Way 2: Using clean_fieldname function using which you can clean your form data for each field separately.
class ArticleForm(ModelForm):
class Meta:
model = Articels #You need to mention the model name for which you want to create the form
fields = ['content', 'headline'] #Fields you want your form to display
def clean_content(self):
# Put your logic here to clean content
def clean_headline(self):
# Put your logic here to clean headline
Basically you would use clean and clean_fieldname methods to validate your form. This is done to raise any error in forms if a wrong input is submitted. Let's assume you want the article's content to have at least 10 characters. You would add this constraint to clean_content.
class ArticleForm(ModelForm):
class Meta:
model = Articels #You need to mention the model name for which you want to create the form
fields = ['content', 'headline'] #Fields you want your form to display
def clean_content(self):
# Get the value entered by user using cleaned_data dictionary
data_content = self.cleaned_data.get('content')
# Raise error if length of content is less than 10
if len(data_content) < 10:
raise forms.ValidationError("Content should be min. 10 characters long")
return data_content
So here's the flow:
Step 1: User open the page say /home/, and you show the user a form to add new article.
Step 2: User submits the form (content length is less than 10).
Step 3: You create an instance of the form using the POST data. Like this form = ArticleForm(request.POST).
Step 4: Now you call the is_valid method on the form to check if its valid.
Step 5: Now the clean_content comes in play. When you call is_valid, it will check if the content entered by user is min. 10 characters or not. If not it will raise an error.
This is how you can validate your form.
What he mean is that with ModelForm you can not only create model instance from QueryDict, but also do a bunch of validation on data types and it's requirements as for example if value's length correct, if it's required etc. Also you will pass only needed data from QueryDict to model instance and not whole request
So typical flow for this is:
form = ModelForm(request.POST)
if form.is_valid():
form.save()
return HttpResponse('blah-blah success message')
else:
form = ModelForm()
return HttpResponse('blah-blah error message')
And awesome Django docs for this: https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#django.forms.ModelForm

django ModelForm save() method issue

I have a model form:
class SnippetForm(ModelForm):
class Meta:
model = Snippet
exclude = ['author', 'slug']
and I want to be able to edit a particular instance by using this:
def edit_snippet(request, snippet_id):
#look up for that snippet
snippet = get_object_or_404(Snippet, pk=snippet_id)
if request.user.id != snippet.author.id:
return HttpResponseForbidden()
if request.method == 'POST':
form = SnippetForm(data=request.POST, instance=snippet)
if form.is_valid():
form.save()
return HttpResponseRedirect(snippet.get_absolute_url())
else:
form = SnippetForm(instance=snippet)
return render_to_response(SNIPPET_EDIT_TEMPLATE,
{'form':form, 'add':False, 'user':request.user},
RequestContext(request))
Notice that at the line
form = SnippetForm(data=request.POST, instance=snippet)
, I created a form that use the data supplied from the user, and bound it with the instance found using the primary key (received from the url). According to django documentation, when I call save() the existing instance should be updated with POSTED data. Instead, what I see is a new object is created and saved into the database. What went wrong? Thanks a lot.
[Edit] This is really embarrassed. The code indeed has nothing wrong with it. The only thing that messed up the whole thing was the action I put in the template (as I use a same template for add and edit a snippet)....Thanks a lot for your help, really appreciate that.
I don't see why it would happen. What version of django is it?
In any case, you can manually force update passing the corresponding argument.
form = SnippetForm(data=request.POST, instance=snippet, force_update=True)

Categories