There are 2 forms on one page.
There are 2 models: 1. Product. 2. SpeciallyPrice. SpeciallyPrice is linked via FK to Product. At the same time, SpeciallyPrice is Inline model in Product.
The fields of the SpecialPriceForm are automatically created using JS. That is, them may be the n-th number. It is necessary to create a record for each created field. In principle, I guess how to do it - use the cycle to drive the values obtained. But the problem is that for some reason None comes from the form. Please help me.
class ProductsCreate(CreateView):
model = Product
form_class = ProductCreateForm
http_method_names = ['get', 'post']
def get_initial(self):
initial = super(ProductsCreate, self).get_initial()
initial['request'] = self.request
return initial
def get_context_data(self, *args, **kwargs):
ctx=super(ProductsCreate, self).get_context_data(*args, **kwargs)
ctx['special_form'] = SpeciallyPriceForm()
return ctx
def get(self, request, *args, **kwargs):
self.object = None
if kwargs.get('slug'):
category = Category.objects.filter(slug=kwargs.get('slug')).first()
self.initial.update({'category': category})
return self.render_to_response(self.get_context_data())
def post(self, request, *args, **kwargs):
self.object = None
form = ProductCreateForm(request.POST, request.FILES, initial={'request': request})
special_form = SpeciallyPriceForm(request.POST)
print(special_form) #Template of form, without values.
if form.is_valid() and special_form.is_valid():
return self.form_valid(form, special_form)
else:
return self.form_invalid(form, special_form)
def form_valid(self, form, special_form):
product = form.save(commit=False)
product.user = self.request.user
product.save()
special = special_form.save(commit=False)
#Here I think, depending on the number of list items, to cycle through it and create the corresponding number of `special` records associated with this` product`. Is the logic correct?
special.product = product
special.save()
for spec_price in special_form.cleaned_data.get('adittional_specially_price'):
print(spec_price)
special.adittional_specially_price = spec_price
for spec_numb in special_form.cleaned_data.get('adittional_specially_number'):
print(spec_numb)
special.adittional_specially_number = spec_numb
forms
class SpeciallyPriceForm(forms.ModelForm):
class Meta:
model = SpeciallyPrice
fields = ['adittional_specially_price', 'adittional_specially_number']
def clean(self):
cleaned_data = super(SpeciallyPriceForm, self).clean()
cd_adittional_specially_price = cleaned_data.get('adittional_specially_price')
print(cd_adittional_specially_price) #None
cd_adittional_specially_number = cleaned_data.get('adittional_specially_number')
print(cd_adittional_specially_number) #None
template + js
<html><body>
Special price from {{ special_form.adittional_specially_price }} kg {{ special_form.adittional_specially_number }} usd
<script>
(function(){
var copy = document.querySelector('.field.inline.specially').cloneNode(true);
document.querySelector('html').addEventListener('input', function(e){
if(e.target.classList.contains('event') && e.target.tagName == 'INPUT'){
var error = 0;
for(var evt of document.querySelectorAll('.field.inline.specially input.event')){
evt.value = evt.value.replace(/[^\d]/,'');
if(!evt.value || +evt.value < 1) error++;
}
if(!error){
var last = document.querySelectorAll('.field.inline.specially');
last[last.length-1].insertAdjacentHTML('afterEnd', copy.outerHTML);
}
}
});
})();
</script>
</body></html>
This form i get in views, when print form for checking
<label for="specially" class="subhead">Special price from</label>
<span class="id_specially_price"><input type="text" name="adittional_specially_price" style="width: 165px" class="event" id="id_adittional_specially_price"></span>
<span>kg</span>
<span class="id_specially_number"><input type="text" name="adittional_specially_number" style="width: 100px" class="event" id="id_adittional_specially_number"></span>
<span>usd</span>
I looked in the views - the form is rendered there, but only with one field, and not with everything. And the form is empty .. How to solve this issue? Maybe Ajax should be connected and it somehow process the request? Or is there a more Django's option?
Answering this bit of comments: "And really come the last two empty created with JS fields. How to make all fields come, tell me, please?"
To save an inline formset in a CBV:
def form_valid(self, form):
context = self.get_context_data()
inline_form = context['inline_form']
if inline_form.is_valid():
self.object = form.save()
inline_form.instance = self.object
inline_form.save()
Clearly you'll have to use the right name for inline_form within the context.
Related
I am working on a bookstore project using django and javascript. I want to allow user to add comments on each book and send the comment without reloading the page using JSON
html:
<h3 id="authorname">{{book.authorname}}</h3>
<h1 id="bookname"> {{book.name}}</h1>
<p id="slogan">-{{book.slogan}}-</p>
<h2 id="price">Price:- <span>${{book.price}}</span></h2>
<form id="addcomment">
{% csrf_token %}
<input hidden name='thecommentuser' value="{{ request.user.id }}" id="commentsubmitter">
<input hidden name='thecommentbook' value="{{ book.id }}" id="commentbook">
<textarea name='thecomment'id="comment"></textarea>
<input type="submit">
</form>
script:
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('#addcomment').onsubmit = function() {
fetch('/comments', {
method: 'POST',
body: JSON.stringify({
thecomment: document.querySelector('#comment').value,
thecommentuser: document.querySelector('#commentsubmitter').value,
thecommentbook: document.querySelector('#commentbook').value,
})})}});
models.py:
class books(models.Model):
bookuser= models.ForeignKey('User', on_delete=models.CASCADE, default= None, blank=True)
name = models.CharField(max_length=80)
slogan = models.CharField(max_length=200, null=True,
price = models.IntegerField()
authorname= models.CharField(max_length=30)
class comments(models.Model):
comment = models.CharField(max_length= 100)
commentuser = models.ForeignKey('User', on_delete=models.CASCADE, default=None)
commentbook = models.ForeignKey('books', on_delete=models.CASCADE, default=None)
def serialize(self):
return {
'thecomment': self.comment,
'thecommentuser': self.commentuser,
'thecommentbook': self.commentbook
}
urls.py:
urlpatterns = [
path('book/<int:id>', views.book, name='book'),
path('comments', views.addcomments, name='comments')
]
views.py:
from django.views.decorators.csrf import csrf_exempt
from .models import *
from django.contrib.auth.decorators import login_required
import json
def book(request, id):
submitedbook = books.objects.get(id=id)
bkchapters = chapters.objects.filter(chapterbook=submitedbook)
bkrewards = rewards.objects.filter(rewardbook = submitedbook)
return render(request, 'network/book.html', {'book':submitedbook, 'chapters':bkchapters, 'rewards':bkrewards})
#csrf_exempt
#login_required
def addcomments(request):
if request.method == 'POST':
print('posted')
data= json.loads(request.body)
comment = data.get('thecomment', '')
commentuser = data.get('thecommentuser')
commentbook = data.get('thecommentbook', '')
cuser = User.objects.get(id = commentuser)
cbook = books.objects.get(id = commentbook)
thecommentt = comments(
thecomment=comment,
thecommentuser=cuser,
thecommentbook=cbook
)
thecommentt.save()
when I open the comments in the admin page I don't find any submitted data and the page reload upon submitting a comment. the order of print('posted') in views.py isn't displayed to me upon adding a comment and that means that the request isn't sent but I don't know why
this appears to me in the terminal window upon adding the comment:
[30/Oct/2022 01:47:19] "GET /book/1?csrfmiddlewaretoken=9QgGiDOUnVxMlxZQ6UkSmiO4auq5BLojV6iWW55qE9LER929Qj7WB8LVtkBfpnJ4&thecommentuser=1&thecommentbook=1&thecomment=Book HTTP/1.1" 200 14927
I tried to delete if request.method == 'POST': in views and to print in views but nothing changed. Also I tried to console.log('submitted') on submitting the comment but nothing appeared
new update:
I find that the error comes from the browser itself as I find that data is sent upon submitting from another browser so although the two browsers are updated. so how could I solve this?
I'm trying to create a page with ability to add any amount of form-copy.
I use django-smart-selects to make my form's field chained. It works fine if I have only 1 form on page.
Then I'm using javascript to make a function to clone form instance by pressing button and addind new form's ids to these clones.
The problem is when I press "+" button I get new cloned form on page, but chained-selection doesn't work anymore(only first one still works), and it seems that this clone copying all my choices from the firs form and remembering this state.
I see in terminal this response every time I choose any selection in chained fields:
[31/Oct/2022 16:42:27] "GET /chaining/filter/cash_table/LevelOne/flowtype/cash_table/CashFlowPattern/level_one/1/ HTTP/1.1" 200 115
[31/Oct/2022 16:42:29] "GET /chaining/filter/cash_table/LevelOne/flowtype/cash_table/CashFlowPattern/level_one/2/ HTTP/1.1" 200 105
But in cloned forms it doesn't happen.
My Formset is:
forms.py
from django import forms
from .models import CashFlowPattern
from django.forms import modelformset_factory
class CashFlowForm(forms.ModelForm):
class Meta:
model = CashFlowPattern
fields = '__all__'
CashFowFormSet = modelformset_factory(
CashFlowPattern,
fields=(
'flow_type',
'level_one',
'level_two',
'eom',
'amount',
'comment'
),
extra=1
)
views.py
class FormAddView(TemplateView):
template_name = 'cash_table/index.html'
def get(self, *args, **kwargs):
formset = CashFowFormSet(queryset=CashFlowPattern.objects.none())
return self.render_to_response({'formset': formset})
def post(self, *args, **kwargs):
end_of_month = (datetime.datetime.now() + relativedelta(day=31)).strftime('%Y-%m-%d')
formset = CashFowFormSet(data=self.request.POST)
if formset.is_valid():
forms = formset.save(commit=False)
for form in forms:
form.eom = end_of_month
form.user = self.request.user
form.save()
# return redirect(reverse_lazy("bird_list"))
return self.render_to_response({'formset': formset})
template:
<form id="form-container" method="POST">
{% csrf_token %}
{{ formset.management_form }}
<div class="empty-form">
{% for form in formset %}
{{ form.media.js }}
<p>{{ form }}</p>
{% endfor %}
</div>
<button id="add-form" type="button">+</button>
<button type="submit" class="btn btn-primary">Отправить</button>
</form>
<script>
let emptyForm = document.querySelectorAll(".empty-form")
let container = document.querySelector("#form-container")
let addButton = document.querySelector("#add-form")
let totalForms = document.querySelector("#id_form-TOTAL_FORMS")
let formNum = emptyForm.length-1
addButton.addEventListener('click', addForm)
function addForm(e) {
e.preventDefault()
let newForm = emptyForm[0].cloneNode(true) //Clone the form
let formRegex = RegExp(`form-(\\d){1}-`,'g') //Regex to find all instances of the form number
formNum++ //Increment the form number
newForm.innerHTML = newForm.innerHTML.replace(formRegex, `form-${formNum}-`) //Update the new form to have the correct form number
container.insertBefore(newForm, addButton) //Insert the new form at the end of the list of forms
totalForms.setAttribute('value', `${formNum+1}`) //Increment the number of total forms in the management form
}
</script>
In my flask app, I need to render a form that has a MultipleSelectField with some already selected options. I have done this successfully but the problem is that they are not highlighted and thus the user can't know which options are already selected which can also enable them be deselected.
Any ideas on how I can get this done from the backend or even using vanilla javascript.
Here is the view function that renders the form:
#home.route('/edit/<int:id>', methods=['GET', 'POST'])
#login_required
def edit_post(id):
post = Post.query.get_or_404(id)
if current_user != post.author and not current_user.can(Permission.ADMINISTER):
abort(403)
form = PostForm()
form.tag.choices = [(tag.id, tag.name) for tag in Tag.query.order_by('name')]
if form.validate_on_submit():
post.body = form.body.data
posttags = db.session.query(categories).filter_by(post_id=id).all()
for id in form.tag.data:
if (post.id, id) not in posttags:
post.tags.append(Tag.query.get(id))
db.session.commit()
return redirect(url_for('.view_post', id=post.id))
form.body.data = post.body
# already selected values
form.tag.data = [tag_id for tag_id in db.session.query(categories).filter_by(post_id=post.id).all()]
return render_template('home/edit_post.html', form=form, title='Edit Post')
Have done some research. So, in a nutshell, how can I add the selected attribute to the pre-selected options.
I'm new to coding and django and I'm struggling to find the solution to the following problem having reviewed the answers I've found.
Im creating a search form with multiple fields. When the user selects the first field category (and before hitting search) I would like to dynamically change the queryset for the second field sub_category such that only related values are shown.
I have models.py as follows:
class Product(models.Model):
category = models.ForeignKey("Category")
sub_category = models.ForeignKey("SubCategory")
class Category(models.Model):
name = models.CharField(max_length=256)
class SubCategory(models.Model):
category = models.ForeignKey("Category")
name = models.CharField(max_length=256)
And my forms.py includes:
class BasicSearchForm(forms.Form):
category = forms.ModelChoiceField(
label='Category',
queryset=Category.objects.all(),
to_field_name="name",
empty_label=None,
initial="Red")
sub_category = forms.ModelMultipleChoiceField(
required=False,
label='Type',
queryset= SubCategory.objects.all(),
to_field_name="name",
widget=forms.Select)
And my views.py includes:
def search(request):
if request.method == 'POST':
form = BasicSearchForm(request.POST)
if form.is_valid():
category = form.cleaned_data['category']
sub_category = form.cleaned_data['sub_category']
return render(request, 'myapp/search.html', {'form': form})
else:
form = BasicSearchForm()
return render(request, 'myapp/search.html', {'form': form})
And finally the search.html includes:
<form class="search-form" role="search" action="/search/" method="get">
{{ form }}
<input type="submit" value="Search" />
</form>
I've played around with a few answers but nothing seems to work. I'd really appreciate some help. Thanks in advance!
Update:
Thanks for the feedback. As a result I updated the following:
In my urls.py:
urlpatterns = [
url(r'^ajax/update_subcategories/$', views.update_subcategories, name='update_subcategories'),
And in my views.py:
def update_subcategories(request):
category = request.GET.get('category', None)
sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
return JsonResponse(sub_category, safe=False)
And I have this in my myapp/search.html:
{% block javascript %}
<script>
$("#id_category").change(function () {
var category = $(this).val();
$.ajax({
url: '{% url "myapp:update_subcategories" %}',
data: {
'category': category,
},
success: function (response) {
var new_options = response;
alert(new_options[0].name); // works
$('#id_sub_category').empty();
$.each(new_options, function(key, value) {
$('#id_sub_category')
.append($('<option>', { value : key })
.text(value.name));
});
}
});
</script>
{% endblock %}
Update: The sub_category options were showing as [object Object] until I changed value to value.name and it looks like it's working. I'll test it out and close unless there are any comments.
Update: Im still having an issue with the browser back button. When a user clicks back the dropdown values have changed back to the original queryset rather than the updated version.
You can't do this from Django views side, ie, backend. You could try an ajax request for implementing this kind of requests, by sending a GET request to the server for populating the drop-down or whatever you are into.
For a simple example, you could refer
here
How do I POST with jQuery/Ajax in Django?
EDIT
def update_subcategories(request):
category = request.GET.get('category', None)
sub_category = list(SubCategory.objects.filter(category__name__exact=category).values('name'))
return JsonResponse(dict(sub_category=sub_category))
Then in ajax response you could grab it like response.data.sub_category
Use ajax to send the category and retrieve subcategory elements.
For the category, send it via get request, and using the orm return the subcategories in a json format which you can show using jQuery.
I have a django form being rendered on an html page, and I have a select dropdown menu which performs a filtering function. I was wondering if it was possible to incorporate this select tag as part of my form? I'd like the select tag to be part of routestep_form.
<center>
<form class ="subtitle" method = 'POST' action="{% url 'buildpage:partrequestinfo' %}" enctype="multipart/form-data">{% csrf_token %}
{{routestep_form.as_p}}
<select name = "myselect" id="id_step" onchange="getOptions(this.value)">
<option value="-----">-----</option>
{% for step in steps %}
<option value="{{ step }}">{{ step }}</option>
{% endfor %}
</select>
<br/><br/>
<select id="id_step_option">
<option value="-----">-----</option>
</select>
<input type='submit' value='Next'/>
</form>
</center>
My Form:
class RouteStepForm(forms.ModelForm):
class Meta:
model = RouteStep
fields = '__all__'
widgets = {
'step': Select,
'step_option': Select,
}
error_messages = {
NON_FIELD_ERRORS: {
'unique_together': "%(RouteStep)s %(description)s are not unique.",
}
}
Views: The other forms are forms I had on the same page, my main focus though is on RouteStepForm.
def partrequestinfo(request):
steps = Step.objects.all()
if not request.user.is_staff or not request.user.is_superuser:
raise Http404
req_form = PartRequestForm(request.POST or None, request.FILES or None)
step_form = StepForm(request.POST or None, request.FILES or None)
stepoption_form = StepOptionForm(request.POST or None, request.FILES or None)
routestep_form = RouteStepForm(request.POST or None, request.FILES or None)
if req_form.is_valid() and step_form.is_valid() and stepoption_form.is_valid() and routestep_form.is_valid():
instance = req_form.save(commit=False)
instance.user = request.user
instance.save()
step = step_form.save(commit=False)
step.save()
stepoption = stepoption_form.save(commit=False)
stepoption.save()
routestep = routestep_form.save(commit=False)
routestep.save()
messages.success(request, "Successfully Created")
return HttpResponseRedirect(instance.get_absolute_url())
else:
context = {
"req_form": req_form,
"step_form": step_form,
"stepoption_form": stepoption_form,
"routestep_form": routestep_form,
"steps": steps,
}
return render(request, "buildpage/partrequestinfo.html", context)
Models:
class Step(models.Model):
name = models.CharField(_('Step Name'), max_length=100, default='')
def __unicode__ (self):
return self.name
class StepOption(models.Model):
step = models.ForeignKey(Step, related_name = 'Step', null = True)
name = models.CharField(_('Step Option'), max_length=100, default='')
def __unicode__ (self):
return self.name + " - " + self.step.name
class RouteStep(models.Model):
step_number = models.PositiveIntegerField(_('Step Number'), default = 0)
step = models.ForeignKey(Step, related_name = 'Step+', null = True)
step_option = models.ForeignKey(StepOption, related_name = 'StepOption', null=True)
def __unicode__ (self):
return self.step_option
Try:
class RouteStepForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(forms.ModelForm, self).__init__(*args, **kwargs)
self.fields['step'].widget.choices=[ query.name for query in Step.objects.all()])
If it does not work, please post your template code that show up.
EDIT
I edit my answer, and also find this helpful.Why do I get an object is not iterable error? and this Getting object not iterable error in django form.
Another solution will be Can not iterate a ChoiceField with Select as widget