django multiple objects update - django

I have a model I want to update multiple objects field 'value' with the same 'substage' field value. At the moment I know how to update one object field 'value' with UpdateView but I do not know how to do it for multiple objects which has the same 'substage' number. Should I use a form ?
class ZoneSubStage(models.Model):
substage = models.ForeignKey(SubStage)
value = models.PositiveSmallIntegerField(default=0)
This is what I do at the moment:
class ZoneSubStageUpdate(UpdateView):
model = ZoneSubStage
fields = ['value']
template_name = 'autostages/zonesubstage_update.html'
zonesubstage_update.html
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Update" />
</form>

If this is your model:
class ZoneSubStage(models.Model):
substage = models.ForeignKey(SubStage)
value = models.PositiveSmallIntegerField(default=0)
Super override the form_valid method to call the update on all related substage objects, using substage_set if you want to set them all to the same value. You can use the Django ORM update() method:
class ZoneSubStageUpdate(UpdateView):
model = ZoneSubStage
fields = ['value']
template_name = 'autostages/zonesubstage_update.html'
def form_valid(self, form):
self.object.substage_set.update(myfield='new-value')
return super(ZoneSubStageUpdate, self).form_valid(form)

Related

Choice Field Instance Not Displaying Correct Data But Other Fields Are

I am trying to display a ModelForm with prepopulated instance data.
It works fine except for the ChoiceField which always displays the first choice given in forms.py ('LP') rather than the choice provided by the instance.
View:
def review(request):
order = Order.objects.get(user__pk=request.user.id)
form = ProjectReviewForm(instance=order)
context = {
'form': form,
}
return render(request, 'users/projectreview.html', context)
Forms:
class ReviewForm(forms.ModelForm):
LAND = 'LP' // #INSTANCE ALWAYS SHOWS THIS RATHER THAN INSTANCE DATA
DATA = 'DC'
STATIC = 'SW'
CHOICES = (
(LAND, 'LP'),
(DATA, 'DC'),
(STATIC, 'SW')
)
product = forms.ChoiceField(choices=CHOICES, widget=forms.Select(attrs={'class': 'form-field w-input'}),)
class Meta:
model = Order
fields = '__all__'
template:
<form method="POST" class="contact-form">
{% csrf_token %}
<h2 class="form-heading-small">Please make sure everything you've submitted is correct.</h2>
{{ form }}
<button type="submit" data-wait="Please wait..." class="button w-button">Looks good!</button>
</form>
The product field on the form is overriding the field on the model. Look into using a ModelChoiceField

How to update a specific manytomany field in django

My Area model has an exercise attribute with a ManyToManyField to my Exercise model:
class Area(models.Model):
name = models.CharField(max_length=100)
exercise = models.ManyToManyField(Exercise)
class Exercise(models.Model):
name = models.CharField(max_length=100)
My AreaView displays a list of areas, which each link to their own list of specific exercises, shown by AreaDetailView:
class AreaView(ListView):
model = Area
template_name = 'workouts/areas.html'
class AreaDetailView(DetailView):
model = Area
template_name = 'workouts/exercises.html'
def get_context_data(self, **kwargs):
context = super(AreaDetailView, self).get_context_data(**kwargs)
context['e_form'] = AddExerciseForm
return context
e.g:
areas.html
- abs
- biceps
- cardio
- legs ...
exercises.html
Abs
- Ab-wheel
- Cable-crunch
- Plank ...
Biceps
- Barbell curl
- Cable curl
- Dumbbell curl
AreaDetailView also displays a form which I would like to allow the user to create their own exercises, which will be specific to their corresponding area.
Here is my form:
class AddExerciseForm(forms.ModelForm):
class Meta:
model = Exercise
fields = ['name']
My template:
<form action="{% url 'exercise_add_new' %}" method="post">
{% csrf_token %}
{{ e_form }}
<button type="submit">Save changes</button>
</form>
My url:
path('exercise-add-new', ExerciseFormView.as_view(), name='exercise_add_new'),
And here is my CreateView which is supposed to handle the logic:
class ExerciseFormView(CreateView):
form_class = AddExerciseForm
success_url = '/'
def form_valid(self, form):
form.save()
new_ex = Exercise.objects.latest('id')
area = Area.objects.get(id=1)
area.exercise.add(new_ex)
return super(ExerciseFormView, self).form_valid(form)
This allows me to update the first object in my Area model ok, but I need to adjust the value of the variable area in form_valid so that the current 'id' is updated. For example if I click on 'Biceps' and then complete the form, I want to add an exercise related to 'id=2'
I have tried area = Area.objects.get(id=self.kwargs['id'])and other similar variations but so far nothing I have tried has worked
In ExerciseFormView are you trying to add a new exercise to an area or create a new area?
If adding a new exercise you will have to pass the area-id from the URL something like add_exercise/<area_id>, if doing the latter it should be straightforward.
You have pass area-id in URL you can do like below
path('exercise-add-new/<int:area_id>/', ExerciseFormView.as_view(),
name='exercise_add_new')
Then update your view as below
def form_valid(self, form):
form.save()
area = Area.objects.get(pk=self.kwargs["area_id"])
new_ex = Exercise.objects.latest('id')
area.exercise.add(new_ex)
return super(ExerciseFormView, self).form_valid(form)
Also update template as :
<form method="post">
{% csrf_token %}
{{ e_form }}
<button type="submit">Save changes</button>
</form>

Django CheckboxSelectMultiple override 'choices' from ModelForm

I would like to be able to extract different information in my django form:
That's my form:
<form action="" method="post">{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit" />
</form>
class InstanceForm(ModelForm):
class Meta:
model = models.BaseAsset
widgets = {
'labels': LabelIconCheckboxSelectMultiple()
}
The model:
class AssetClass(models.Model):
default_labels = models.ManyToManyField(Label, null=True, blank=True)
pass
the M2M reference field
class Label(models.Model):
explanation = models.CharField(null=True, max_length=63)
svgpreview = models.CharField(null=True, max_length=31)
def __unicode__(self):
return unicode(self.explanation)
pass
Now, the HTML code generated by the {{ form.as_p }} is as follows:
<li><label for="id_labels_0"><input type="checkbox" name="labels" value="1" id="id_labels_0" /> Consult owner before using</label></li>
<li><label for="id_labels_1"><input type="checkbox" name="labels" value="2" id="id_labels_1" /> This item is broken</label></li>
Which means it's clearly using the __unicode__ rendering of the model 'Label'. How can I change that behavior in the Select widget, so that it would use a different function to populate it's choices? I'm trying to get it, in the reasonably portable way, to print '<img src="{{label.svgpreview}}" alt="{{label.explanation}}"...>' next to the checkbox?
You will override forms.widgets.CheckboxSelectMultiple class:
This is CheckboxSelectMultiple class and its render function:
class CheckboxSelectMultiple(SelectMultiple):
def render(self, name, value, attrs=None, choices=()):
if value is None: value = []
has_id = attrs and 'id' in attrs
final_attrs = self.build_attrs(attrs, name=name)
output = [u'<ul>']
# Normalize to strings
str_values = set([force_unicode(v) for v in value])
for i, (option_value, option_label) in enumerate(chain(self.choices, choices)):
# If an ID attribute was given, add a numeric index as a suffix,
# so that the checkboxes don't all have the same ID attribute.
if has_id:
final_attrs = dict(final_attrs, id='%s_%s' % (attrs['id'], i))
label_for = u' for="%s"' % final_attrs['id']
else:
label_for = ''
cb = CheckboxInput(final_attrs, check_test=lambda value: value in str_values)
option_value = force_unicode(option_value)
rendered_cb = cb.render(name, option_value)
option_label = conditional_escape(force_unicode(option_label))
output.append(u'<li><label%s>%s %s</label></li>' % (label_for, rendered_cb, option_label))
output.append(u'</ul>')
return mark_safe(u'\n'.join(output))
So what you will do :
class MyCheckboxSelectMultiple(CheckboxSelectMultiple):
def render(self, name, value, attrs=None, choices=()):
#put your code to have custom checkbox control with icon
#...
output.append(u'<li><label%s>%s %s</label></li>' % (label_for, rendered_cb, option_label)) # especially you will be working on this line
#...
Then where you are using widgets=CheckboxSelectMultiple() it will become widgets=MyCheckboxSelectMultiple()
Reading django.forms.models.ModelChoiceField gives a hint:
# this method will be used to create object labels by the QuerySetIterator.
# Override it to customize the label.
def label_from_instance(self, obj):
"""
This method is used to convert objects into strings; it's used to
generate the labels for the choices presented by this object. Subclasses
can override this method to customize the display of the choices.
"""
return smart_unicode(obj)
ok, but how do I override it per-instance of ModelForm - this gets overridden in few places throughout django.forms
Considering the following code:
class InstanceForm(ModelForm):
class Meta:
model = models.BaseAsset
widgets = {
'labels': forms.CheckboxSelectMultiple()
}
def __init__(self, *args, **kwargs):
def new_label_from_instance(self, obj):
return obj.svgpreview
super(InstanceForm, self).__init__(*args, **kwargs)
funcType = type(self.fields['labels'].label_from_instance)
self.fields['labels'].label_from_instance = funcType(new_label_from_instance, self.fields['labels'], forms.models.ModelMultipleChoiceField)
This is somewhat creepy - basically, it's a more bizzare implementation of this:
Override a method at instance level
Please read the comments in the referenced thread to understand why this might be a bad idea in general..
You don't have to do the "creepy" instance-level override to take proper advantage of the documented django.forms.models.ModelChoiceField.label_from_instance() method.
Building on the AssetClass and Label objects in the original post:
class AssetSvgMultiField(forms.ModelMultipleChoiceField):
"""
Custom ModelMultipleChoiceField that labels instances with their svgpreview.
"""
def label_from_instance(self, obj):
return obj.svgpreview
class InstanceForm(forms.ModelForm):
default_labels = AssetSvgMultiField(queryset=Label.objects.all())
class Meta:
model = models.AssetClass
widgets = {
'default_labels': forms.CheckboxSelectMultiple()
}
This is explained in the Django documentation here:
https://docs.djangoproject.com/en/1.9/ref/forms/fields/#django.forms.ModelChoiceField.to_field_name
You can see the ModelChoiceField class calling the method on the field here:
https://github.com/django/django/blob/1155843a41af589a856efe8e671a796866430049/django/forms/models.py#L1174
If you're not overriding choices explicitly, then your code might look like this:
class RectificationAssetMultiField(forms.ModelMultipleChoiceField):
def label_from_instance(self, obj):
return '[{0.pk}] {0.label} ({0.location})'.format(obj)
class RectificationForm(forms.ModelForm):
items = RectificationAssetMultiField(
required=False,
queryset=InspectionItem.objects.all(),
widget=forms.CheckboxSelectMultiple,
label="Non-compliant Assets"
)
class Meta:
model = Rectification
fields = ('ref', 'items', 'status')
Be careful that this will only work if you're not setting choices directly (see _get_choices in the above URL).
If instead you wanted to override choices (for a more efficient result than a queryset, or something better expressed as a ValuesList) then you would have something like this:
class RectificationAssetMultiField(forms.ModelMultipleChoiceField):
def label_from_instance(self, obj):
return '[{0.pk}] {0.label} ({0.location})'.format(obj)
class RectificationForm(forms.ModelForm):
items = RectificationAssetMultiField(
required=False,
queryset=InspectionItem.objects.none(),
widget=forms.CheckboxSelectMultiple,
label="Non-compliant Assets"
)
def __init__(self, *args, **kwargs):
super(RectificationForm, self).__init__(*args, **kwargs)
self.fields['items'].choices = (InspectionItem.objects
.active()
.noncompliant()
.filter(property_id=self.instance.property_id)
.values_list('pk', 'label') # pass a key value pair
)
class Meta:
model = Rectification
fields = ('ref', 'items', 'status')
Don't use {{ form.as_p }} if you don't like that rendering.
Loop over the form instead:
<form action="/contact/" method="post">
{% for field in form %}
<div class="fieldWrapper">
{{ field.errors }}
{{ field.label_tag }}: {{ field }}
</div>
{% endfor %}
<p><input type="submit" value="Send message" /></p>
</form>
You are then free to use whatever HTML you want.
From: https://docs.djangoproject.com/en/dev/topics/forms/#looping-over-the-form-s-fields

Trying to pass a QuerySet as initial data to a formset

I'm trying to build a page for an inventory system that will allow a user to update a quantity of items received.
I want to show a table of all products and let the user enter the quantity received, which I'll post and iterate over to update the database.
Here is my view:
def new_shipment(request):
list_of_active_products = Product.objects.filter(status=1)
ShipmentFormSet = formset_factory(ShipmentForm, extra=0)
formset = ShipmentFormSet(initial=list_of_active_products)
return render_to_response('inventory/new_shipment.html', {'formset': formset})
Here's my model for the form:
class ShipmentForm(forms.Form):
sku = forms.IntegerField()
product_name = forms.CharField(max_length=100)
quantity = forms.IntegerField()
And here is the form template:
<form method="post" action="">
<table>
{% for form in formset %}
{{ form }}
{% endfor %}
</table>
<input type="submit" />
</form>
And here is the error I'm getting:
Caught AttributeError while rendering: 'Product' object has no attribute 'get'
Can anyone help me out with this?
From the docs it looks like you have to pass in a list of dictionaries as the initial data, rather than a QuerySet:
Also note that we are passing in a list of dictionaries as the initial data.
You may want to change your initial query to:
list_of_active_products = Product.objects.filter(status=1).values()
which will return a list of dictionaries rather than model-instance objects.
Using initial data with a formset:
https://docs.djangoproject.com/en/dev/topics/forms/formsets/#using-initial-data-with-a-formset
ValuesQuerySet:
https://docs.djangoproject.com/en/dev/ref/models/querysets/#django.db.models.query.QuerySet.values
You can also use the queryset argument. This should work:
formset = ShipmentFormSet(queryset=list_of_active_products)
cf. https://docs.djangoproject.com/en/dev/topics/forms/modelforms/#changing-the-queryset

Django and users entering data

I am building a webapp which will be used by a company to carry out their daily operations. Things like sending invoices, tracking accounts receivable, tracking inventory (and therefore products). I have several models set up in my various apps to handle the different parts of the web-app. I will also be setting up permissions so that managers can edit more fields than, say, an office assistant.
This brings me to my question. How can I show all fields of a model and have some that can be edited and some that cannot be edited, and still save the model instance?
For example, I have a systems model for tracking systems (we install irrigation systems). The system ID is the primary key, and it is important for the user to see. However, they cannot change that ID since it would mess things up. Now, I have a view for displaying my models via a form using the "form.as_table". This is efficient, but merely spits out all the model fields with input fields filling in the values stored for that model instance. This includes the systemID field which should not be editable.
Because I don't want the user to edit the systemID field, I tried making it just a label within the html form, but django complains. Here's some code:
my model (not all of it, but some of it):
class System(models.Model):
systemID = models.CharField(max_length=10, primary_key=True, verbose_name = 'System ID')
systemOwner = models.ForeignKey (System_Owner)
installDate = models.DateField()
projectManager = models.ForeignKey(Employee, blank=True, null=True)
#more fields....
Then, my view for a specific model instance:
def system_details(request, systemID):
if request.method == 'POST':
sysEdit = System.objects.get(pk=systemID)
form = System_Form(request.POST, instance=sysEdit)
if form.is_valid():
form.save()
return HttpResponseRedirect('/systems/')
else:
sysView = System.objects.get(pk=systemID)
form = System_Form(instance=sysView)
return render_to_response('pages/systems/system_details.html', {'form': form}, context_instance=RequestContext(request))
Now the html page which displays the form:
<form action="" method="POST">
{% csrf_token %}
<table>
{{ form.as_table }}
</table>
<input type="submit" value="Save Changes">
<input type="button" value="Cancel Changes" onclick="window.location.href='/systems/'">
</form>
So, what I am thinking of doing is having two functions for the html. One is a form for displaying only those fields the user can edit, and the other is for just displaying the content of the field (the systemID). Then, in the view, when I want to save the changes the user made, I would do:
sysValues = System.objects.get(pk=SystemID)
form.save(commit = false)
form.pk = sysValues.sysValues.pk (or whatever the code is to assign the sysValues.pk to form.pk)
Is there an easier way to do this or would this be the best?
Thanks
One thing you can do is exclude the field you don't need in your form:
class System_Form(forms.ModelForm):
class Meta:
exclude = ('systemID',)
The other is to use read-only fields: http://docs.djangoproject.com/en/1.3/ref/contrib/admin/#django.contrib.admin.ModelAdmin.readonly_fields as #DTing suggessted
To make a field read only you can set the widget readonly attribute to True.
using your example:
class System_Form(ModelForm):
def __init__(self, *args, **kwargs):
super(System_Form, self).__init__(*args, **kwargs)
self.fields['systemID'].widget.attrs['readonly'] = True
class Meta:
model = System
or exclude the fields using exclude or fields in the class Meta of your form and display it in your template if desired like so:
forms.py
class System_Form(ModelForms):
class Meta:
model = System
exclude = ('systemID',)
views.py
def some_view(request, system_id):
system = System.objects.get(pk=system_id)
if request.method == 'POST':
form = System_Form(request.POST, instance=system)
if form.is_valid():
form.save()
return HttpResponse('Success')
else:
form = System_Form(instance=system)
context = { 'system':system,
'form':form, }
return render_to_response('some_template.html', context,
context_instance=RequestContext(request))
some_template.html
<p>make changes for {{ system }} with ID {{ system.systemID }}</p>
<form method='post'>
{{ form.as_p }}
<input type='submit' value='Submit'>
</form>