Multi field and computed value - django

I need a solution for this problem:
I would like to have MultiField widget for a "value" field. This field should allow to input two values (two input widgets), and user should choose which of these values is to be saved (two checkbox widgets). However, one of these values must be recalculated with respect to other field's value.
I've taken this approach:
a MultiValueField with 4 fields:
class PriceDetailField(MultiValueField):
use_net = BooleanField(required=False)
net_value = DecimalField(required=False, decimal_places=2)
use_gross = BooleanField(required=False)
gross_value = DecimalField(required=False, decimal_places=2)
a MultiWidget:
class PriceDetailWidget(MultiWidget):
use_net = CheckboxInput()
net_value_widget = TextInput()
use_gross = CheckboxInput()
gross_value_widget = TextInput()
and a custom Form...
class PriceModelForm(ModelForm):
value = PriceDetailField()
...which overrides default model form for a model:
class Price(models.Model):
value = models.DecimalField(
max_digits=19,
decimal_places=2,
default=Decimal(0),
)
However, this solution seems to be messed up. In the form, I need to input all subfields (the MultiValueField subfields), otherwise "Please enter value" error appears (even though those fields are marked as required=False). Also,
I must recalculate the mentioned value upon a save, having returned a tuple from the field with information which checkbox was checked and the corresponding text value, then replace the tuple with the decimal value in clean_value method of form (also, saving the checkboxes state in temporary fields....). I think such a design is very weak.
The form should work both on its own and as inline (this means, the value of the field which is used to calculate the returned value can or cannot change during save).
Is such a thing even possible?
And the root of the problem: I want to store prices of items as net prices, but I would like to allow users to input them as net or gross prices, and then recalculate gross price to net price with respect to product's VAT tax level (since VAT is assigned to product or service, not to the price). A product can have many prices, so a price is backlinked to the product by a foreign key.
Cheers,
Tomek

The feature you're looking for on a MultiValueField (allowing the sub-fields to be required or not individually) is logged as a feature request on Django.
You could probably fix this for now by subclassing MultiValueField with a rewritten clean method based on the original and following suggestions from the bug report. You're not going to have a lot of the original MultiValueField left by that point though, and if you do, you should submit your patch to Django and put a note on the bug.
Ignoring for a moment the requiredness thing, have you written a compress method on your PriceDetailField? What does it look like? This is where you should be doing the work to turn your four sub-fields into a single field to save.

Related

Join two records from same model in django queryset

Been searching the web for a couple hours now looking for a solution but nothing quite fits what I am looking for.
I have one model (simplified):
class SimpleModel(Model):
name = CharField('Name', unique=True)
date = DateField()
amount = FloatField()
I have two dates; date_one and date_two.
I would like a single queryset with a row for each name in the Model, with each row showing:
{'name': name, 'date_one': date_one, 'date_two': date_two, 'amount_one': amount_one, 'amount_two': amount_two, 'change': amount_two - amount_one}
Reason being I would like to be able to find the rank of amount_one, amount_two, and change, using sort or filters on that single queryset.
I know I could create a list of dictionaries from two separate querysets then sort on that and get the ranks from the index values ...
but perhaps nievely I feel like there should be a DB solution using one queryset that would be faster.
union seemed promising but you cannot perform some simple operations like filter after that
I think I could perhaps split name into its own Model and generate queryset with related fields, but I'd prefer not to change the schema at this stage. Also, I only have access to sqlite.
appreciate any help!
Your current model forces you to have ONE name associated with ONE date and ONE amount. Because name is unique=True, you literally cannot have two dates associated with the same name
So if you want to be able to have several dates/amounts associated with a name, there are several ways to proceed
Idea 1: If there will only be 2 dates and 2 amounts, simply add a second date field and a second amount field
Idea 2: If there can be an infinite number of days and amounts, you'll have to change your model to reflect it, by having :
A model for your names
A model for your days and amounts, with a foreign key to your names
Idea 3: You could keep the same model and simply remove the unique constraint, but that's a recipe for mistakes
Based on your choice, you'll then have several ways of querying what you need. It depends on your final model structure. The best way to go would be to create custom model methods that query the 2 dates/amount, format an array and return it

django setting filter field with a variable

I show a model of sales that can be aggregated by different fields through a form. Products, clients, categories, etc.
view_by_choice = filter_opts.cleaned_data["view_by_choice"]
sales = sales.values(view_by_choice).annotate(........).order_by(......)
In the same form I have a string input where the user can filter the results. By "product code" for example.
input_code = filter_opts.cleaned_data["filter_code"]
sales = sales.filter(prod_code__icontains=input_code)
What I want to do is filter the queryset "sales" by the input_code, defining the field dynamically from the view_by_choice variable.
Something like:
sales = sales.filter(VARIABLE__icontains=input_code)
Is it possible to do this? Thanks in advance.
You can make use of dictionary unpacking [PEP-448] here:
sales = sales.filter(
**{'{}__icontains'.format(view_by_choice): input_code}
)
Given that view_by_choice for example contains 'foo', we thus first make a dictionary { 'foo__icontains': input_code }, and then we unpack that as named parameter with the two consecutive asterisks (**).
That being said, I strongly advice you to do some validation on the view_by_choice: ensure that the number of valid options is limited. Otherwise a user might inject malicious field names, lookups, etc. to exploit data from your database that should remain hidden.
For example if you model has a ForeignKey named owner to the User model, he/she could use owner__email, and thus start trying to find out what emails are in the database by generating a large number of queries and each time looking what values that query returned.

Django: Joining on fields other than IDs (Using a date field in one model to pull data from a second model)

I'm attempting to use Django to build a simple website. I have a set of blog posts that have a date field attached to indicate the day they were published. I have a table that contains a list of dates and temperatures. On each post, I would like to display the temperature on the day it was published.
The two models are as follows:
class Post(models.Model):
title = models.CharField(max_length=200)
text = models.TextField()
date = models.DateField()
class Temperature(models.Model):
date = models.DateField()
temperature = models.IntegerField()
I would like to be able to reference the temperature field from the second table using the date field from the first. Is this possible?
In SQL, this is a simple query. I would do the following:
Select temperature from Temperature t join Post p on t.date = p.date
I think I really have two questions:
Is it possible to brute force this, even if it's not best practice? I've googled a lot and tried using raw sql and objects.extra, but can't get them to do what I want. I'm also wary of relying on them for the long haul.
Since this seems to be a simple task, it seems likely that I'm overcomplicating it by having my models set up sub-optimally. Is there something I'm missing about how I should design my models? That is, what's the best practice for doing something like this? (I've successfully pulled the temperature into my blog post by using a foreign key in the Temperature model. But if I go that route, I don't see how I could easily make sure that my temperature dates get the correct foreign key assigned to them so that the temperature date maps to the correct post date.)
There will likely be better answers than this one, but I'll throw in my 2ยข anyway.
You could try a property inside the Post model that returns the temperature:
#property
def temperature(self):
try:
return Temperature.objects.values_list('temperature',flat=True).get(date=self.date)
except:
return None
(code not tested)
About your Models:
If you will be displaying the temperature in a Post list (a list of Posts with their temperatures), then maybe it will be simpler to code and a faster query to just add a temperature field to your Post model.
You can keep the Temperature model. Then:
Assuming you have the temperature data already present in you Temperature model at the time of Post instance creation, you can fill that new field in a custom save method.
If you get temperature data after Post creation, you cann fill in that new temperature field through a background job (maybe triggered by crontab or similar).
Sometimes database orthogonality (not repeating info in many tables) is not the best strategy. Just something to think about, depending on how often you will be querying the Post models and how simple you want to keep that query code.
I think this might be a basic approach to solve the problem
post_dates = Post.objects.all().values('date')
result_temprature = Temperature.objects.filter(date__in = post_dates).values('temperature')
Subqueries could be your friend here. Something like the following should work:
from django.db.models import OuterRef, Subquery
temps = Temperature.objects.filter(date=OuterRef('date'))
posts = Post.objects.annotate(temperature=Subquery(temps.values('temperature')[:1]))
for post in posts:
temperature = post.temperature
Then you can just iterate through posts and access the temperature off each post instance

Update multiple results at once in Django

I want to update budget of Category model in Django.
class Category(models.Model):
user = models.ForeignKey(User)
name = models.CharField(max_length=128)
budget = models.DecimalField(default=0.0, decimal_places=2, max_digits=12, help_text="Amount in dollars ($)")
I would have a list of new budget like this which is equal to number of data in Category:
>>> new_category_budget
[Decimal('2700.000'), Decimal('750.000'), Decimal('1500.000')]
I am updating like this:
>>> for budget in new_category_budget:
... Category.objects.filter(user=2).update(budget=budget)
...
3L
3L
3L
But all of these return the same data. What's wrong with my update statement?
What you're doing is iterating over your budget values, and updating all records with user=2 to each one, overriding the previous value.
The return value of QuerySet.update is the number of updated records. Each time you call update(), you get the result that 3 records were updated.
I don't quite understand what you are actually trying to do, but it might be something like this (untested!):
for (budget, category) in zip(new_category_budget, list(Category.objects.filter(user=2)):
category.budget=budget
category.save()
Of course, this assumes that the number of filtered categories will exactly match the number of budgets in new_category_budget, and also the order of iteration over categories is not obvious. All in all, this seems weird :)
When you call update on a QuerySet, it will set all items in the QuerySet to that value. See the example here.
So through your for loop, you are updating all the Category objects with user=2 to each budget. At the end of the for loop, all the Category objects should be have budget == new_category_budget[-1] or the last budget item.
If you want different values for each Category object, you'll need to call save on them individually.

Displaying properties in Django admin - translating their names

In my Django application my model has some values set as properties - they are calculated on demand from other values (like, min. value of some other objects' field, etc). This works pretty well as I don't need to store those in the database and the calculations can be expensive, so they're cached.
So I have a model:
class A(models.Model):
name = models.TextField(_('Name'))
def max_of_some_values(self):
# calculate it here, with caching,etc
return 1
max_value = property(max_of_some_values)
When I show this in my admin application, on the object list the name column is displaying using it's translation. So in Polish it's Nazwa, English it's Name, etc.
At the same time I found no way of adding a translated 'column' name for my property.
Anyone handled this before?
You can set a short_description property on the method to determine the column name - I believe it should be possible to mark this as translatable.
def max_of_some_values(self):
# calculate it here, with caching,etc
return 1
max_of_some_values.short_description = _('Max value')