With Django Listview, is it possible to display new columns that contain the values of floor division and modulo?
I have the following two models:
models.py
class Model_Item(models.Model):
item_name = models.CharField(max_length = 100, null = False, blank = False, unique = True)
item_bottleperpack = models.FloatField(null = True, blank = False) # e.g. 100 bottles per pack
def __unicode__(self):
return self.item_name
class Model_ItemTransaction(models.Model):
item = models.ForeignKey(Model_Item, to_field = "item_name")
item_sold = models.FloatField(null = True, blank = True) # in terms of bottle
def __unicode__(self):
return self.item
With this listview:
views.py
class View_Item(ListView):
def get_queryset(self):
queryset = Model_Item.objects.all()
queryset = queryset.annotate(
sum_ = Sum("model_itemtransaction__item_sold")
)
queryset = queryset.annotate(
floor_division_ = F("sum_") // F("item_bottleperpack"),
module_ = F("sum_") %% F("item_bottleperpack")
)
return queryset
Basically, if I have sold, say 650 bottles, and there is 100 bottles per pack, I would like the listview to display:
6 packs on the "floor-division" column, and
50 bottles on the "modulo" column.
Currently I am receiving the following errors with my current code
unsupported operand type(s) for //: 'F' and 'F'
and
unsupported operand type(s) for %%: 'F' and 'F'
and hoping someone could help me find any solution to this (and it does not have to be with conditional expression).
According to https://docs.djangoproject.com/en/2.0/_modules/django/db/models/expressions/:
Neither class F nor it's parent Combinable implement __floordiv__, so // is not implemented. Combinable does implement __truediv__, which is the / operator.
I haven't heard of the operator %% in Python, but % will work because __mod__ is implemented in Combinable.
Try using models.IntegerField or models.PositiveIntegerField for item_bottleperpack and item_sold, and updating this line in views.py:
queryset = queryset.annotate(
floor_division_ = F("sum_") / F("item_bottleperpack"),
module_ = F("sum_") % F("item_bottleperpack")
)
I'm assuming that Combinable will return an integer as the output of dividing two integers -- this might not be the case. If so, you might want to round down the result.
So I have found a way to solve this, thanks to this post.
First I cleaned the code at views.py,
views.py
class View_Item(ListView):
def get_queryset(self):
queryset = Model_Item.objects.all()
queryset = queryset.annotate(
sum_ = Sum("model_itemtransaction__item_sold")
)
return queryset
then added this code file into the templatetags folder,
app_filters.py
from django import template
register = template.Library()
#register.filter(name = "func_floor_division")
def func_floor_division(num, val):
if num:
floor_division = num // val
return floor_division
else:
return None
#register.filter(name = "func_modulo")
def func_modulo(num, val):
if num:
modulo = num % val
return modulo
else:
return None
and finally modified the tags in the html file.
html
{% load app_filters %}
<table>
<tr>
<th>Floor Division</th>
<th>Modulo</th>
</tr>
{% for obj_ in object_list %}
<tr>
<td>{{ obj_.sum_|func_floor_division:obj_.item_bottleperpack }}</td>
<td>{{ obj_.sum_|func_modulo:obj_.item_bottleperpack }}</td>
</tr>
{% endfor %}
</table>
Hope this helps for anyone who is encountering the same issue.
you can create a new floor_division and module_ fields in your Model_Item, then you use the post save receiver to update the floor_division and module_ fields
from django.db.models.signals import pre_save
class Model_Item(models.Model):
item_name = models.CharField(max_length = 100, null = False, blank = False, unique = True)
item_bottleperpack = models.FloatField(null = True, blank = False) # e.g. 100 bottles per pack
floor_division = models.IntegerField(null=True, blank=True)
module_ = models.IntegerField(null=True, blank=True)
def __unicode__(self):
return self.item_name
def model_item_pre_save(sender, instance, created):
item = Model_Item.objects.get(id=instance.id)
item_transaction = Model_Transaction.objects.get(item_id=instance.id)
item.floor_division = item_transaction.item_sold // item.item_bottleperpack
item.module_ = item_transaction.item_sold %% item.item_bottleperpack
pre_save.connect(model_item_pre_save, sender=Model_Item)
Related
For a personnal project, I have a view that regroups several froms. For a spacial reason a had to make a list and in that list I added some forms depending on the data in my DB. So my question is : how can I check the validation a those forms that are present in the list.
Here is my view :
def confirmation_view(request, id ,*args, **kwargs):
tournament = Tournament.objects.get(pk=id)
sport = tournament.sport
rule = Rule.objects.get(tournament=tournament)
categories = Category.objects.filter(tournament=tournament)
form_tournament = TournamentCreationForm(request.POST or None, instance=tournament)
form_sport = SportEditForm(request.POST or None, instance=sport)
form_rule = RuleForm(request.POST or None, instance=rule)
enum = 1
tab_form_category = []
for category in categories:
form_category = CategoryForm(request.POST or None, instance=category)
tab_form_category.insert(enum, form_category)
enum = enum + 1
if form_tournament.is_valid() and form_sport.is_valid() and form_rule.is_valid():
return redirect('tournament')
context = {
'form_tournament': form_tournament,
'form_sport': form_sport,
'form_rule': form_rule,
'tab_form_category': tab_form_category
}
return render(request, 'confirmation.html', context)
I speak about "tab_form_category".
I don't have enough experience in Python and Django to deduce the solution by my self.
EDIT:
Here is my Category model:
class Category(models.Model):
description = models.CharField(max_length=128)
tournament = models.ForeignKey(Tournament, default=None, on_delete=models.CASCADE)
Forms.py
CategorySubForm(forms.ModelForm):
class Meta:
model = Category
fields = ['description']
TournamentCategoryFormset = forms.inlineformset_factory(Tournament, Category, form=CategorySubForm, min_num=1, extra=0, can_delete=False)
View.py
def confirmation_view(request, id ,*args, **kwargs):
tournament = Tournament.objects.get(pk=id)
sport = tournament.sport
rule = Rule.objects.get(tournament=tournament)
form_tournament = TournamentCreationForm(request.POST or None, instance=tournament)
form_sport = SportEditForm(request.POST or None, instance=sport)
form_rule = RuleForm(request.POST or None, instance=rule)
category_formset = TournamentCategoryFormset(request.POST or None, instance=tournament)
if form_tournament.is_valid() and form_sport.is_valid() and form_rule.is_valid() and if category_formset.is_valid() :
category_formset.save()
return redirect('tournament')
context = {
'form_tournament': form_tournament,
'form_sport': form_sport,
'form_rule': form_rule,
'tab_form_category': tab_form_category
'categories' : category_formset
}
return render(request, 'confirmation.html', context)
in your template:
{% for category_form in categories%}
{{category_form.as_p}}
{% endfor %}
{{categories.management_form}}
Edit:
In your case you need to look at inlineFormset: https://docs.djangoproject.com/en/2.2/topics/forms/modelforms/#inline-formsets
It's designed to work with related form (you will have one subform for each category associated to the tournament).
If you are using a many to many between tournament and category, you need to associate the intermediary table (through)
I am trying to make changes in two models at the same time. So, according to one topic on the forum, I created two forms in one view and added it to one html.
I think I am doing something wrong in my second form. Why my value in my model does not change to False?
It looks more or less like that.
views.pl
if request.method == 'POST' and 'btn_massage_order' in request.POST:
ordering_form = OrderingMassageForm(data=request.POST)
if ordering_form.is_valid():
ordering = ordering_form.save(commit=False)
massage_product = query_product # nazwa produkty
masseurs = query_user # massage
massage_time_interval = time # time example 60 min
price_massage = price # price
day_week = clean_my_date # day week
time_compartment = ordering_form.cleaned_data['time']
[...]
ordering.massage_product = massage_product
ordering.masseurs = masseurs
ordering.massage_time_interval = massage_time_interval
ordering.time_compartment = time_compartment
ordering.price = price_massage
ordering.day_week = day_week
[...]
ordering.save()
else:
ordering_form = OrderingMassageForm()
#next form i views
if request.method == 'POST' and 'btn_massage_order' in request.POST:
ordering_form = OrderingMassageForm(data=request.POST)
ordering_form_on_off = TimeOnTimeOff(data=request.POST)
if ordering_form_on_off.is_valid() and ordering_form.is_valid():
ordering_form_on_off = ordering_form_on_off.save(commit=False)
# the value that will be save
reservation = False
# I receive my object
time_compartment = ordering_form.cleaned_data['time']
# assigning my object
ordering_form_on_off.time_compartment = reservation
#save
ordering_form_on_off.save()
else:
ordering_form_on_off = TimeOnTimeOff()
forms.py
class OrderingMassageForm(forms.ModelForm):
class Meta:
model = OrderingMassage
fields = ('time',
'place',
'payment_method',
'name',
'surname',
[...]
class TimeOnTimeOff(forms.ModelForm):
class Meta:
model = Time
fields = ('free_or_no',
)
widgets = {
'free_or_no': forms.HiddenInput(),
}
models.py
(in which I try to change the value through a second form that does not work)
class Time(models.Model):
day_time = models.ForeignKey(DayTime, on_delete=models.CASCADE)
compartment = models.CharField(max_length=11)
free_or_no = models.BooleanField(default=True)
time_equivalent = models.IntegerField()
template
<form action="." method="post">
{% csrf_token %}
{{ ordering_form.as_p }}
<button type="submit" name="btn_massage_order" class="btn btn-primary">Potwierdż rezerwacje</button>
</form>
Any help will be appreciated.
I am trying to filter tours based on the date in Django 2.0, i.e. to show all the tours that start on or after June 01, 2019 (should be received from a form).
I have a model 'Tour':
class Tour(models.Model):
destination = models.ForeignKey(Destination, on_delete=models.PROTECT)
Created = models.DateTimeField(auto_now_add=True)
Updated = models.DateTimeField(auto_now=True)
Available = models.BooleanField(default=True, help_text="Tick it if you still operate this tour")
And I have a model 'Departures':
class Departures(models.Model):
tour = models.ForeignKey(Tour, on_delete=models.CASCADE)
DepartingDate = models.DateField(auto_now=False)
FinishingDate = models.DateField(auto_now=False)
DepartureStatus = models.CharField(max_length=200, choices = DEPARTURE_STATUS, help_text="Select from the list")
PlacesAvailable = models.BooleanField("Places avalable", default="True")
DepartureGaranteed = models.BooleanField("Departure Garanteed", default="True")
I want the user should select a start date of the tour from the html form:
<div class="form-group">
<input class="form-control" id="dateStart" name="DateStart" type="date"/>
</div>
I have a form in my models.py:
class SearchForm(forms.Form):
Country = forms.CharField(max_length=255, required=False)
TourType = forms.CharField(max_length=255, required=False)
TourTheme = forms.CharField(max_length=255, required=False)
Duration = forms.IntegerField(required=False) #, blank=True
Price = forms.CharField(max_length=255, required=False) #, blank=True
DateStart = forms.DateField(required=False) #input_formats='%d/%m/%Y',
EDITED(added the view function):
def search_tours(request):
if request.method == 'GET':
form = SearchForm(request.GET)
if form.is_valid():
Country = form.cleaned_data['Country']
TourType = form.cleaned_data['TourType']
TourTheme = form.cleaned_data['TourTheme']
Duration = form.cleaned_data['Duration']
Price = form.cleaned_data['Price']
DateStart = form.cleaned_data['DateStart']
#Convert the date format
# DateStart = datetime.strptime(str(DateStart), '%Y-%m-%d')
# DateStart.strftime('%Y/%m/%d')
# DateStart.strftime("%Y-%m-%d")
departures = Departures.objects.all()
myfilter = Q()
if Country is not None:
for c in Country:
myfilter &= Q(destination__Country__icontains=c)
if TourType is not None:
myfilter &= Q(TourType__contains=TourType)
if TourTheme is not None:
myfilter &= Q(TourTheme__contains=TourTheme)
if Duration is not None:
myfilter &= Q(Duration__lte=Duration)
if Price in request.GET:
myfilter &= Q(Price__lte=Price)
if DateStart in request.GET:
myfilter &= Q(Departures__DepartingDate__gt=DateStart)
tours = Tour.objects.filter(myfilter)
paginator = Paginator(tours, 3) # Show 3 tours per page
page = request.GET.get('page')
tours = paginator.get_page(page)
args = {'tours': tours, 'departures': departures,
'Country': Country,
'TourType': TourType,
'TourTheme': TourTheme,
'Price' : Price,
'Duration' : Duration,
}
return render(request, 'tours/search_tours.html', args)
else:
return HttpResponseRedirect('../error/')
else:
form = SearchTourForm()
tours = Tour.objects.filter(Available = True)
return render(request, 'tours/search_tours.html', {
'tours': tours,
})
So I am trying to filter the tours like below:
tours = Tour.objects.filter(Q(Departures__DepartingDate__gt=DateStart))
But it is not working. I guess the problem is that the html form date format and the django db date format are not matching. It does not give an error, but it does not filter. Just shows all the tour.
How can I bring those two dates to the same format and compare? How to fix this bug?
So what I did to solve the question was that I change the model name from 'Departure' to 'departure':
if DateStart is not None:
myfilter &= Q(departures__DepartingDate__gt=DateStart)
I also changed the condition for the above if statement from
if DateStart in request.GET:
to
if DateStart is not None:
Have a nice time! ;)
I am passing the foreign key id in the values_list to the template and so my template can only show the id of the referenced object. I would like to get the actual object though without explicitly passing the fields.
models.py:
class Transaction(models.Model):
''' This class will host RBC raw data. '''
account_type = models.CharField(max_length = 20)
account_number = models.CharField(max_length = 20)
transaction_date = models.DateField()
cheque_number = models.CharField(max_length = 20, null = True, blank = True)
description_1 = models.CharField(max_length = 100, null = True, blank = True)
description_2 = models.CharField(max_length = 100, null = True, blank = True)
cad = models.DecimalField(max_digits = 10, decimal_places = 2, blank = True, null = True)
usd = models.DecimalField(max_digits = 10, decimal_places = 2, blank = True, null = True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank = True, null = True, on_delete = models.PROTECT)
category = models.ForeignKey('BudgetCategory', blank = True, null = True, on_delete = models.PROTECT) # same as above comment
subcategory = models.ForeignKey('BudgetSubCategory', blank = True, null = True, on_delete = models.PROTECT) # Does not delete related sub-categories if a transaction is delted.
views.py:
transaction_fields = ['account_type', 'account_number', 'transaction_date', 'description_1', 'description_2','cad', 'category', 'subcategory']
field_order_by = ['category', 'subcategory', 'description_1', 'transaction_date']
class AllRecordsView(ListView):
template_name = 'index.html'
context_object_name = 'transactions'
fields = transaction_fields
field_order_by = field_order_by
def get_queryset(self, *args, **kwargs):
transactions = Transaction.objects.all().values_list(*self.fields).order_by(*field_order_by)
return transactions
def get_context_data(self, **kwargs):
context = super(AllRecordsView, self).get_context_data(**kwargs)
column_fields = fields_to_columns(self.fields) # returns the name of columns in capitalized form and changing underscore to space
context['fields'] = column_fields # Adding a context variable called field which hosts the column names list
return context
Template:
{% for transaction in transactions %}
<tr>
{% for field in transaction %}
<td> {{ field }} </td>
</tr>
In other words, I am trying to avoid passing all transaction objects and calling each field separately:
<tr>
{% for transaction in transactions %}
<td> {{ transaction.account_type }} </td>
...
<td>
{{ transaction.category.name }}
</td>
{% endfor %}
</tr>
Is there anyway that I can maintain code versatility and display the foreign key objects' attributes?
You can access traverse relationships by using the double underscore notation similar to how you do in a filter call. Such as:
Class A:
b = ForeignKey(B)
class B:
description = CharField()
A.objects.all().values_list('b__description')
So for yours it seems like you'd change transaction_fields to include a 'related_obj__field_needed' value in the list.
As commented below, I will try to explain my problem more precisely with more code examples
In my application I am using a model inheritance, where base model class is:
class Entity(models.Model):
VISIBILITY_LEVELS = (
('0', 'Private'),
('1', 'Closedy'),
('2', 'Public'),
)
entityType = models.CharField(max_length=50)
userCreatedBy = models.ForeignKey(User, related_name='user_createdby', editable=False)
dateCreated = models.DateField(editable=False)
lastModified = models.DateField()
lastModifiedBy = models.ForeignKey(User, related_name='user_lastModifiedBy')
tags = models.ManyToManyField('Tag', blank=True, null=True)
visibilityLevel = models.CharField(max_length=1, choices=VISIBILITY_LEVELS, default=False)
In my form, I am editing a model which derived from Entity:
class Place(Entity):
name = models.CharField(max_length=155)
description = models.TextField()
location = models.ForeignKey(Point)
And the point and tag models are:
class Point(models.Model):
lat = models.FloatField() #coordinates
lng = models.FloatField()
hgt = models.FloatField(default=0.0)
class Tag(models.Model):
tagName = models.CharField(max_length=250) #tag name
usedAmount = models.IntegerField(default = 1) #how many entities has this tag
All of the models have primary key generated automatically by django. In my site, I am using AJAX to process the form (currently, there is no AJAX validation, but it will be ;)
My first problem was: what will be the easiest way for creating a form for adding a new Place object? Where the most tricky part was Tag addition, cause I need to enable both - adding new tags and selecting existing tags. I am new to Django, so some my attempts can be naive.. My solution was to make two Forms inherited from ModelForm for Point and Place and one custom Form for Tag. In tag form, I want to enable user to choose existing tags from DB or to type new tags separated by ';' in a textinput. So I have created those 3 forms:
class PlaceAddForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(PlaceAddForm, self).__init__(*args, **kwargs)
self.fields['name'].label = "Name"
self.fields['description'].label = "Desc"
self.fields['visibilityLevel'].label = "Visibility"
class Meta:
model = Place
fields = ('name', 'description',
'visibilityLevel' )
#--
class PointForm(forms.ModelForm):
class Meta:
model = Point
#---
class TagAppendForm(forms.Form):
newTags = forms.CharField(
widget=forms.TextInput(),
required = False,
)
tagsAll = forms.ModelMultipleChoiceField(
label="Choose:",
queryset=Tag.objects.all(),
required = False
)
def clean_newTags(self):
if len(self.cleaned_data['newTags']) == 0:
return []
tags = self.cleaned_data['newTags'].split(';')
for t in tags:
if len(t) == 0:
raise forms.ValidationError('Tag must be minum 3 characters long')
return tags
And then in my template I have:
<form id="save-form" method="post" action="/user/places/addedit/">
{{ pointForm.as_p }}
{{ placeForm.as_p }}
{{ tagForm.as_p }}
<input type="submit" value="save" />
</form>
And View is:
def place_save_view(request):
ajax = request.GET.has_key('ajax')
if request.method == 'POST':
placeForm = PlaceAddForm(request.POST)
pointForm = PointForm(request.POST)
tagForm = TagAppendForm(request.POST)
if placeForm.is_valid() and tagForm.is_valid() and pointForm.is_valid():
place = _place_save(request, placeForm, pointForm, tagForm)
variables = RequestContext(request, {'places' : place })
return HttpResponseRedirect('/user/places/', variables)
#else:
# if ajax:
elif request.GET.has_key('entity_ptr_id'):
place = Place.objects.get(id==request.GET['entity_ptr_id'])
placeForm = PlaceAddForm(request.GET,instance=place)
point = place.location
pointForm = PointForm(request.GET,instance=point)
tagForm = TagAppendForm(initial={'tagsAll': place.tags.values_list('id', flat=True)})
else:
placeForm = PlaceAddForm()
pointForm = PointForm()
tagForm = TagAppendForm()
variables = RequestContext(request, {'placeForm': placeForm, 'pointForm': pointForm, 'tagForm': tagForm })
if ajax:
return render_to_response('places/place_save.html', variables)
else:
return render_to_response('places/add-edit-place.html', variables)
And finally AJAX:
function placeAdd() {
var div = $(document).find("#leftcontent");
div.load("/user/places/addedit/?ajax", null, function() {
$("#save-form").submit(placeSave);
});
return false;
}
function placeSave()
{
var item = $(this).parent();
var data = {
lng: item.find("#id_lng").val(),
lat: item.find("#id_lat").val(),
hgt: item.find("#id_hgt").val(),
name: item.find("#id_name").val(),
description: item.find("#id_description").val(),
pType: item.find("#id_pType").val(),
visibilityLevel: item.find("#id_visibilityLevel").val(),
newTags: item.find("#id_newTags").val(),
tagsAll: item.find("#id_tagsAll").val()
};
$.post("/user/places/addedit/?ajax", data, function(result){
if(result != "failure")
{
//todo....
}
else {
alert("Failure!");
}
});
return false;
}
Here I have more than a topic question to ask:
Is a better solution for doing that? (ok, I can create a one custom form but I loose some automation parts...)
Why after submitting form with blank newTags and no selection on tagsAll fields, I have an error for the tagsAll field (it appears on form, above tagsAll field):
"null" is not a valid value for a primary key
I have also a problem with displaying errors on form, when AJAX is used... :/ I find solution, that I must manually iterate through them on template with form (http://infinite-sushi.com/2011/06/using-ajax-with-django/).. For any other solution I will be grateful :)