I need to update and, if needed, create elements in a Django update view. Basically, I have a form where I am giving the user the chance of updating a row or inserting one or more new rows.
The problem is that I am having issues in updating the "old" rows. If I update an existing row, it creates a new one.
Here I post some code:
views.py
def edit_flight_mission(request, pk):
mission = Mission.objects.get(id=pk)
form = EditMissionForm(request.POST or None, instance=mission)
learning_objectives = LearningObjective.objects.filter(mission_id=mission)
context = {
'mission': mission,
'form': form,
'learning_objectives': learning_objectives,
}
if request.method == 'POST':
learning_obj = request.POST.getlist('learning_obj')
solo_flight = request.POST.get('solo_flight')
if form.is_valid():
mission_obj = form.save()
if solo_flight == 'solo_flight':
mission_obj.solo_flight = True
mission_obj.save()
for lo in learning_obj:
learning_objective, created = LearningObjective.objects.get_or_create(name=lo, mission_id=mission.id)
if not created:
learning_objective.name = lo
learning_objective.save()
return render(request, 'user/edit_flight_mission.html', context)
models.py
class Mission(models.Model):
name = models.CharField(max_length=200)
duration_dual = models.DurationField(blank=True, null=True)
duration_solo = models.DurationField(blank=True, null=True)
training_course = models.ForeignKey(
TrainingCourse, on_delete=models.CASCADE)
note = models.TextField(null=True, blank=True)
solo_flight = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class LearningObjective(models.Model):
name = models.CharField(max_length=300)
mission = models.ForeignKey(Mission, on_delete=models.CASCADE, blank=True, null=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
forms.py
class EditMissionForm(forms.ModelForm):
class Meta:
model = Mission
fields = ('name', 'duration_dual', 'duration_solo', 'training_course')
widgets = {
'name': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Enter Mission Name'}),
'duration_dual': forms.TextInput(attrs={'class':'form-control', 'placeholder': 'Duration as HH:MM:SS'}),
'duration_solo': forms.TextInput(attrs={'class':'form-control', 'placeholder': 'Duration as HH:MM:SS'}),
'training_course': forms.Select(attrs={'class': 'form-control'}),
}
template
{% extends "base.html" %}
{% block head_title %}
Edit Flight Mission {{mission.id}}
{% endblock head_title %}
{% block title %}
Edit Flight Mission {{mission.id}}
{% endblock title%}
{% block content %}
<form action="" method="post">
{% csrf_token %}
<div class="card-body">
<div class="form-group">
{{form.as_p}}
</div>
<div class="form-group">
<div id="inputFormRow">
<label>Learning Objective</label>
{% for lo in learning_objectives %}
<div class="input-group mb-3">
<input type="text" value="{{lo.name}}" class="form-control" name="learning_obj" placeholder="Learning Objective">
<div class="input-group-append">
</div>
</div>
{% endfor %}
<div id="newRow"></div>
<div class="form group">
<div class="form-check">
<input class="form-check-input" type="checkbox" name="solo_flight" value="solo_flight" id="flexCheckDefault">
<label class="form-check-label" for="flexCheckDefault">
Solo Flight
</label>
</div>
</div>
<button id="addRow" type="button" class="btn btn-primary mb-3">Add Learning Objective</button>
</div>
</div>
<div class="card-footer">
<button type="submit" class="btn btn-primary btn-block">
Add New Mission
</button>
</div>
</form>
{% endblock content %}
{% block custom_js %}
<script type="text/javascript">
// add row
$("#addRow").click(function () {
var html = '';
html += '<div id="inputFormRow">';
html += '<div class="input-group mb-3">'
html += '<input type="text" class="form-control" name="learning_obj" placeholder="Learning Objective">'
html += '<div class="input-group-append">'
html += '<button class="btn btn-danger" type="button" id="remove">Remove</button>'
html += '</div></div>'
$('#newRow').append(html);
});
// remove row
$(document).on('click', '#remove', function () {
$(this).closest('#inputFormRow').remove();
});
</script>
{% endblock custom_js %}
The form is updating correctly but the problem is with the part concerning the Learning Objectives basically.
Any suggestion?
The problem is here:
learning_objective, created = LearningObjective.objects.get_or_create(name=lo, mission_id=mission.id)
Specifically, the mission_id=mission.id part. If you want to do a lookup to a ForeignKey, you need two underscores. Therefore, the query is not finding the LearningObjective, thus it is always creating a new one. But it's not even necessary, since you've already filtered learning_objectives by mission (and there, it was done with the correct syntax).
The solution, then is to do this:
learning_objective, created = LearningObjective.objects.get_or_create(name=lo)
if not created:
learning_objective.name = lo
learning_objective.save()
The solution, though can be done much easier with update_or_create. This is the same as what you're doing, but in one line instead of 4.
learning_objective, created = LearningObjective.objects.update_or_create(name=lo, defaults={'name': lo})
Edit
I think the syntax here is actually not correct. Change it as follows:
# Change this,
# learning_objectives = LearningObjective.objects.filter(mission_id=mission)
# To this:
learning_objectives = LearningObjective.objects.filter(mission=mission)
Edit 2
I'm not sure if this problem is what's causing the learning_objectives not to save, but I now see another error in the html. You can not have a form within another form. The {{ form.as_p }} is creating another <form> tag within the one you already have. So the form is validating because all the fields of the {{ form.as_p }} are there, but those are for the Mission object. Are the other fields even being submitted? Check by print(request.POST). I'm guessing that it will not contain the name field for the learning_obj.
Possible Solutions:
Create a form, but not a ModelForm, that has everything you want to save from the two different models.
Render the {{ form }} manually, so that the <form> tags are not there. When you submit, all inputs with names will be submitted.
Related
I have implemented a Dependent dropdown list within Django but when I try to submit the form I get the following error 'Select a valid choice. That choice is not one of the available choices.'
I have spent a while looking on the web for the answer and have tried a few with little avail.
From my understanding and reading, this is an error because I render the form with a queryset of none. Then I use ajax to fill in the options. Even though I have updated the dropdown list, the form validation is checking my submitted answer against a queryset of none - thus the error.
So i'm hoping someone can help me to update the choices the form will accepted on form submission.
views.py
# stage6 is where I render my view and check validation
def stage6(request):
form_deal = DealForm(request.POST or None, prefix='inst')
if form_deal.is_valid():
form_deal.save()
messages.success(request, 'Deal added successfully.')
form_deal = DealForm()
context = {
'dform': form_deal,
}
return render(request, 'stages/stage6/stage6.html', context)
# This is used for my ajax request
def load_offers(request):
property_process_id = request.GET.get('propertyprocess_link')
offers = Offer.objects.filter(propertyprocess_link=property_process_id).order_by('id')
return render(request, 'stages/stage6/offers_dropdown_list.html', {'offers': offers})
forms.py
class DealForm(forms.ModelForm):
deal_date = forms.CharField(
label='',
widget=forms.TextInput(attrs={'type': 'date'})
)
target_move_date = forms.CharField(
label='',
widget=forms.TextInput(attrs={'type': 'date'})
)
def __init__(self, *args, **kwargs):
super(DealForm, self).__init__(*args, **kwargs)
# filter the foreign keys shown
self.fields['propertyprocess_link'].queryset = PropertyProcess.objects.filter(sector="Sales")
# filter used for ajax request
self.fields['offer_accepted'].queryset = Offer.objects.none()
# add a "form-control" class to each form input
# for enabling bootstrap
for name in self.fields.keys():
self.fields[name].widget.attrs.update({
'class': 'form-control',
})
class Meta:
model = Deal
fields = ('propertyprocess_link',
'deal_date',
'price_agreed',
'target_move_date',
'offer_accepted'
)
models.py
class Deal(models.Model):
propertyprocess_link = models.ForeignKey(PropertyProcess,
on_delete=models.CASCADE)
deal_date = models.DateField()
price_agreed = models.IntegerField()
target_move_date = models.DateField()
offer_accepted = models.ForeignKey(Offer,
on_delete=models.CASCADE)
class Meta:
verbose_name_plural = "deals"
def __str__(self):
return '%s, %s' % (
self.propertyprocess_link.property_link.address_line_1,
self.propertyprocess_link.property_link.postcode
)
html
{% block content %}
<div class="container-fluid header-container">
<div class="row">
<div class="col-sm-9 col-md-7 col-lg-5 mx-auto">
<div class="card-au card-signin my-5">
<div class="card-body">
<form id="offers-form" data-offers-url="{% url 'ajax_load_offers' %}" class=" text-center text-white" method="post" novalidate>
{% csrf_token %}
{{ dform.non_field_errors }}
<div class="form-colour mt-2">
{{ dform.propertyprocess_link.errors }}
<label class="mb-0 mt-1">Property Being Offered On:</label>
{{ dform.propertyprocess_link }}
</div><div class="form-colour mt-2">
{{ dform.offer_accepted.errors }}
<label class="mb-0 mt-1">Offer Being Accepted:</label>
{{ dform.offer_accepted }}
</div>
<div class="form-colour mt-2">
{{ dform.price_agreed.errors }}
<label class="mb-0 mt-1">Price Agreed:</label>
{{ dform.price_agreed }}
</div>
<div class="form-colour mt-2">
{{ dform.deal_date.errors }}
<label class="mb-0 mt-1">Deal Date:</label>
{{ dform.deal_date }}
</div>
<div class="form-colour mt-2">
{{ dform.target_move_date.errors }}
<label class="mb-0 mt-1">Target Move Date:</label>
{{ dform.target_move_date }}
</div>
<div class="mb-3"></div>
{# hidden submit button to enable [enter] key #}
<div class="hidden-btn" style="margin-left: -9999px"><input class="hidden-btn" type="submit" value=""/></div>
<div class="text-center mt-2">
<input type="submit" class="login-btn btn-green btn btn-lg border-green text-uppercase py-3" value="Add Deal" />
</div>
</form>
</div>
</div>
</div>
</div>
</div>
{% endblock content %}
{% block postloadjs %}
{{ block.super }}
<script>
$("#id_inst-propertyprocess_link").change(function () {
var url = $("#offers-form").attr("data-offers-url"); // get the url of the `load_offers` view
var propertyID = $(this).val(); // get the selected Property Process ID from the HTML input
$.ajax({ // initialize an AJAX request
url: url, // set the url of the request (= localhost:8000/ajax/load-offers/)
data: {
'propertyprocess_link': propertyID // add the Property Process id to the GET parameters
},
success: function (data) { // `data` is the return of the `load-offers` view function
$("#id_inst-offer_accepted").html(data); // replace the contents of the offers input with the data that came from the server
}
});
});
</script>
{% endblock postloadjs %}
Thanks very much for any help anyone can give.
I have 2 models, Product and ProductRelatedGroupAndProduct , the Product have 5 data, [chicken, okra, ampalaya, cabbage] and the ProductRelatedGroupAndProduct have [chicken, okra, cabbage] how do i get the remaining data in Product that dont have in ProductRelatedGroupAndProduct ?
the choosen product is ProductRelatedGroupAndProduct and the list of product is Product
I just want that the data displayed on the choosen product should no longer be visible in the product list.
so far this is my views.py
def searchrelatedproduct(request):
id = request.GET.get('relatedproductID')
products = Product.objects.all()
relatedproduct = ProductRelatedGroupAndProduct.objects.filter(productrelatedgroup = id)
return render(request, "customAdmin/relatedproduct.html",{"products":products,"relatedproduct":relatedproduct})
this is my html
<div class="container">
<form method="POST" action="/GroupOfProduct/" enctype="multipart/form-data">{% csrf_token %}
<h2>Product </h2>
<div class="form-group">
<br>
<label for="sel2">List Of Products</label>
<select multiple class="form-control" id="sel2" name="product">
{% for product in products %}
<option>{{product.product}}</option>
{% endfor %}
</select>
</div>
<input type="submit" style="float:right;">
</form>
</div>
<div class="container">
<h2>Choosen Product</h2>
<form method="POST" action="/UpdateGroupOfProduct/" enctype="multipart/form-data">{% csrf_token %}
<input type="submit" style="float:right;">
<div class="form-group">
<br>
<label for="sel2">List Of Choosen Products</label>
<select multiple class="form-control" id="sel2" name="relatedproduct">
{% for product in relatedproduct %}
<option value="{{ product.id }}">{{product.product}}</option>
{% endfor %}
</select>
</div>
</form>
</div>
this is my models.py
class ProductRelatedGroupAndProduct(models.Model):
product = models.ForeignKey(Product,on_delete=models.SET_NULL, null=True,blank=True,verbose_name="Product")
productrelatedgroup = models.ForeignKey('ProductGroup',
on_delete=models.SET_NULL, null=True, blank=True,
verbose_name="ProductGroup")
class Product(models.Model):
product = models.CharField(max_length=500)
class ProductGroup(models.Model):
category = models.CharField(max_length=500, blank=True)
UPDATE
when i tried this
diffproducts = products.exclude(pk__in=relatedproduct)
print(diffproducts)
it prints all data of the Product
<QuerySet [<Product: cabbage>, <Product: ampalaya>, <Product: okra>, <Product: Chicken>]>
and when i tried this
unused_product = set(relatedproduct).difference(set(products))
print(unused_product)
***result***
{<ProductRelatedGroupAndProduct: cabbage>, <ProductRelatedGroupAndProduct: ampalaya>, <ProductRelatedGroupAndProduct: okra>}
and this
Product.objects.filter(productrelatedgroupandproduct__isnull=True)
i get this error
django.core.exceptions.FieldError: Cannot resolve keyword 'productrelatedgroupandproduct' into field
You can use reverse relation for filter:
Product.objects.filter(productrelatedgroupandproduct__isnull=True)
productrelatedgroupandproduct here is a lowercased name of ProductRelatedGroupAndProduct model. It's default value for related_query_name argument.
This could be possible by converting your queryset to a set and then use intersection for complex cases.
# get objects present in both
set(products).intersection(set(relatedproduct))
# get product not in relatedproduct
set(products).difference(set(relatedproduct))
# opposite
set(relatedproduct).difference(set(products))
In your case exclude can do the trick:
products.exclude(pk__in=relatedproduct)
You can use the exclude function :
views.py:
def searchrelatedproduct(request):
id = request.GET.get('relatedproductID')
products = Product.objects.all()
relatedproduct = ProductRelatedGroupAndProduct.objects.filter(pk=id)
diffproducts = products.exclude(pk__in=relatedproduct)
return render(request, "test.html",{"products":diffproducts,"relatedproduct":relatedproduct})
and output in template in your orignal html.
I'm trying to make a django developed website which is called MuyPicky. It is a website which allows you to find restaurants and other things based on your pickiness. I am currently making a page for forms which adds Restaurants to the database. As I am doing this I get this error:
IntegrityError at /restaurants/create.
NOT NULL constraint failed: restaurants_restaurantlocation.name
Is this why I get the error
Here is the forms.py:
class RestaurantCreateForm(forms.Form):
title = forms.CharField()
location = forms.CharField(required = False)
category = forms.CharField(required = False)
The form.html:
{% extends "base.html" %}
{% block title %}Add Restaurant || {{block.super}} {% endblock %}
{% block content %}
<div class="container-fluid">
<div class="container">
<div class="row">
<div class="col-md-offset-4 col-md-4 col-md-offset-4">
<h1>Add Restaurant</h1>
<form method="POST">{% csrf_token %}
<input title="Title" class="form-control" type="text" name="Title" placeholder="Title">
<br>
<input title="Location" class="form-control" type="text" name="Location" placeholder="Location"><br>
<input title="Category" class="form-control" type="text" name="Category" placeholder="Category"><br>
<!--<input title="Save" class="form-control btn btn-info " type="submit" value="Save" >--><br>
<button class="btn btn-success form-control btn-md center-block" type="submit">Save</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
The view from views.py:
def restaurant_createview(request):
#if request.method == "GET":
# print("get data")
if request.method == "POST":
title = request.POST.get("title") #(request.POST["title"])
location = request.POST.get("location")
category = request.POST.get("category")
obj = RestaurantLocation.objects.create(
name = title,
location = location,
category = category
)
return HttpResponseRedirect("/restaurants/")
template_name = "restaurants/form.html"
context = {}
return render(request, template_name, context)
Lastly the urls.py:
urlpatterns = [
url(r'^admin/', admin.site.urls),
url(r'^/$', TemplateView.as_view(template_name="home.html")),
url(r'^restaurants/$', RestaurantListView.as_view()),
url(r'^restaurants/create/$', restaurant_createview),
url(r'^restaurants/(?P<slug>[\w-]+)/$', RestaurantDetailView.as_view()),
url(r'^contact/$', TemplateView.as_view(template_name="contact.html")),
url(r'^about/$',TemplateView.as_view(template_name="about.html"))]
Your fields have name Title, Location, Category but your Django code is looking for title, location and category. These need to be the same.
I have a model with 'created' field. I want store date only when new object is created, but do not update this field when I change some other fields in model. Before sync i had auto_now but I changed it like in first answer Automatic creation date for django model form objects?. My problem is when I update my entity
IntegrityError at /rhyme/15/
null value in column "created" violates not-null constraint
DETAIL: Failing row contains (15, LORE, <p>LORE</p>
<p>LORE</p>
<p>LORE</p>
<p>LORE</p>
<p>LORE</p>
<p>L..., null, 1, 1).
My model:
class Rhyme(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()
created = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(UserProfile, related_name='created_rhymes')
profiles = models.ManyToManyField(UserProfile, related_name='stored_rhymes')
category = models.ForeignKey(Category, null=True, blank=True, related_name='rhymes')
update
view:
form = RhymeForm(data)
form_is_valid = form.is_valid()
if form_is_valid:
rhyme = form.save(commit=False)
if self.kwargs.has_key('id'):
rhyme.id = self.kwargs['id']
rhyme.author = self.request.user.profile
rhyme.save()
form:
class RhymeForm(forms.ModelForm):
title = forms.CharField(
widget=forms.TextInput(attrs={'class':'form-control'}),
label=u'Tytuł'
)
content = forms.CharField(
widget=forms.Textarea(attrs={'class':'form-control'}),
label=u'Treść'
)
category = forms.ModelChoiceField(
queryset=Category.objects.all(),
label=u'Kategoria',
empty_label=" ",
required=False
)
class Meta:
model = Rhyme
fields = ('title', 'content', 'category')
def save(self, commit=True):
rhyme = super(RhymeForm, self).save(commit=False)
if commit is True:
rhyme.save()
return rhyme
template (bit weird but json sends just category, content and title):
<form action="{$ formAction $}" role="form" method="post" ng-submit="add($event)">
{% for field in form.visible_fields %}
<div class="form-group {% if field.errors %}has-error{% endif %}">
{{ field.label_tag }}
{{ field|ngmodel }}
{% with 'errors.'|add:field.name as error_field %}
<div class="text-warning bg bg-danger">
{{ field.errors }}{$ {{ error_field }} $}
</div>
{% endwith %}
</div>
{% endfor %}
{% csrf_token %}
<div class="row">
<button ng-click="close($event)" class="btn btn-default col-md-3">Anuluj</button>
<input class="btn btn-success col-md-offset-1 col-md-8" type="submit" value="Zapisz" />
</div>
</form>
My ugly solution is to put
rhyme.created = models.Rhyme.objects.get(pk=self.kwargs['id']).created
But why my ORM trying to put null to field created, how to solve this?
I have a basic Form that accepts a file / image from a template. For some reason it won't validate and I can't see the error.
views.py
# Flag a Job as complete
#login_required()
#user_passes_test(lambda u: u.groups.filter(name='Developer').exists(), login_url='/login/', redirect_field_name='not allowed')
#require_http_methods(['POST'])
def job_complete(request, jobid, userid):
# Get the Job
job = Jobs.objects.get(id=jobid)
jobsubmit = JobSubmitForm(request.FILES)
if jobsubmit.is_valid():
js = jobsubmit.save(commit=False)
js.user_id = userid
js.job_id = jobid
js.save()
job.status = 'IR'
job.save()
return redirect('view_job', jobid=jobid, slug=job.slug)
else:
messages.error(request, "There are form errors!")
return redirect('view_job', jobid=jobid, slug=job.slug)
forms.py
class JobSubmitForm(forms.ModelForm):
class Meta:
model = JobSubmit
fields = ('file', 'image', 'comment', 'gitHubLink')
def save(self, commit=True):
jobsubmit = super(JobSubmitForm, self).save(commit=False)
jobsubmit.user_id = self.cleaned_data['user_id']
jobsubmit.job_id = self.cleaned_data['job_id']
if commit:
jobsubmit.save()
return jobsubmit
view.html
<form method="post" action="/job/job_complete/j{{ job.id }}/u{{ request.user.id }}/" class="form-inline btn-group" enctype="multipart/form-data">
{% csrf_token %}
<div class="span6 inline">
<label class="control-label">Attach Files: </label>{{ job_submit.file }}
<p class="help-block">Attach files that go with this Job.</p>
</div>
<div class="span6 inline">
<label class="control-label">Attach Images: </label>{{ job_submit.image }}
<p class="help-block">Attach images that go with this Job.</p>
</div>
<div class="span6 inline">
<label class="control-label">GitHub URL: </label>{{ job_submit.gitHubLink|add_class:"form-control"|attr:"placeholder:Example: https://www.github.com/path/to/code/repo/or/commit" }}
<p class="help-block">If hosting work on GitHub.</p>
</div>
<div class="span6 inline"><label class="control-label">Job Comments(Required): </label>{{ job_submit.comment|add_class:"form-control"|attr:"placeholder:Example: Fun Job! Please keep me in mind for future work!" }} </div>
<div class="modal-footer">
<button type="submit" class="btn btn-success btn-med pull-left"><i class="fa fa-check-circle"></i> Job Complete</button>
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</form>
models.py
# Store data related to a Job (files, comments, etc.)
class JobSubmit(models.Model):
job = models.ForeignKey(Jobs)
user = models.ForeignKey(User)
file = models.FileField(upload_to="uploads/jobs/files", blank=True, null=True)
image = models.ImageField(upload_to="uploads/jobs/images", blank=True, null=True)
comment = models.TextField()
gitHubLink = models.URLField(blank=True)
Hopefully it's not something silly... it's been a long day and sleepy programming isn't the best idea. :/
Appreciate the help if anyone sees what's wrong. Pointers welcomed, as well. Cheers,
This line:
jobsubmit = JobSubmitForm(request.FILES)
should be as:
jobsubmit = JobSubmitForm(request.POST, request.FILES)
try it?