Display editable decimal field with trailing currency symbol in admin interface (Django) - python

I have a simple model:
class Product(models.Model):
...
price = models.DecimalField(max_digits=4, decimal_places=2)
...
def price_currency(self):
return '%.2f €' % self.price
And admin model:
class ProductAdmin(admin.ModelAdmin):
list_display = ['price_currency']
Achieves this list overview of products (only price column screenshot):
The problem is that this field is not editable (in list view). Is there a way to make it editable and display the trailing currency symbol at the end?
I can just call list_display = ['price'] and list_editable = ['price'], the problem is that then it does not show the euro sign at the end, ie:
My ideal end goal would be:

Related

Django get last value for each forgin key values

In models.py
class loan(models.Model):
completed=models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True,null=True)
application_id=models.CharField(max_length=100,unique=True)
class topay(models.Model):
loanapplication=models.ForeignKey(loan,on_delete=models.CASCADE,null=True)
paymentdate=models.DateField(null=True, blank=True)
How to get last paymentdate for every loan application
In views.py
topay.objects.filter().values().annotate(last=Max('paymentdate'))
to get each loan latest topay you can filter like this
loan.objects.annotate(
maxpaydate=Max("topay__paymentdate"),
).values("id", "maxpaydate")

Django - How to render a ModelForm with a Select field, specifying a disabled option?

I have the following models:
# Get or create a 'Not selected' category
def get_placeholder_categoy():
category, _ = ListingCategories.objects.get_or_create(category='Not selected')
return category
# Get default's category ID
def get_placeholder_category_id():
return get_placeholder_categoy().id
class ListingCategories(models.Model):
category = models.CharField(max_length=128, unique=True)
def __str__(self):
return f'{self.category}'
class Listing(models.Model):
title = models.CharField(max_length=256)
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name='listings')
description = models.TextField(max_length=5120, blank=True)
img_url = models.URLField(default='https://media.istockphoto.com/vectors/no-image-available-picture-coming-soon-missing-photo-image-vector-id1379257950?b=1&k=20&m=1379257950&s=170667a&w=0&h=RyBlzT5Jt2U87CNkopCku3Use3c_3bsKS3yj6InGx1I=')
category = models.ForeignKey(ListingCategories, on_delete=models.CASCADE, default=get_placeholder_category_id, related_name='listings')
creation_date = models.DateTimeField()
base_price = models.DecimalField(max_digits=10, decimal_places=2, validators=[
MinValueValidator(0.01),
MaxValueValidator(99999999.99)
])
With these, I have the following form:
class ListingForm(ModelForm):
class Meta:
model = Listing
exclude = ['seller', 'creation_date']
widgets = {
'title': TextInput(attrs=base_html_classes),
'description': Textarea(attrs=base_html_classes),
'img_url': URLInput(attrs=base_html_classes),
'category': Select(attrs=base_html_classes),
'base_price': NumberInput(attrs=base_html_classes)
}
One of the available categories I have is "Not selected", since I want to allow that if at some point a category were to be removed, items can be reassigned to that one, however, when rendering the form, I will do some validation on the view function to prevent it from being submitted if the "not selected" category is sent with the form.
Because of this, I want the HTML form on the template to assign the 'disabled' attribute to the option corresponding to that category, however, I have been searching for a couple of days now without finding anything that I was able to understand to the point where I could try it.
Ideally, another thing I'd like to achieve is to be able to modify the order of the rendered options on the form so that I can move to the top 'not selected' regardless of its primary key within the model.
I am aware I can just create a form instead of a model form, or just modify the template so I manually specify how to render the form itself, but I do feel like there is a simple fix to this either on the model or on the model form that I am just not finding yet.
Thanks in advance!
I would suggest you use (in model definition)
class Listing(models.Model):
..
category = model.ForeignKey(ListingCategories, on_delete=models.SET_NULL, null=True, related_name='listings')
..
and optionally in form definition
class ListingForm(ModelForm):
category = forms.ModelChoiceField(ListingCategories, empty_label='Not Selected')
..
While rendering model form, a required attribute will be automatically added, and in form validating, it is also required. It is only in database validation that the field can be left NULL

Django form: Update model values based on user input in one field

I am writing a form to let a user enter a purchase from the template. A couple things need to happen:
the purchase goes to populate a row in the replenishment table
some fields of the replenishment table get updated based on what the user has input
here is what my model look like:
class replenishment(models.Model):
Id = models.CharField(max_length=100, primary_key=True, verbose_name= 'references')
Name = models.CharField(max_length=200)
Quantity = models.FloatField(default=0)
NetAmount = models.FloatField(default=0)
SupplierID = models.CharField(max_length=200)
Supplier = models.CharField(max_length=200)
SellPrice = models.FloatField(default=0)
StockOnOrder = models.FloatField(default=0)
StockOnHand = models.FloatField(default=0)
def __str__(self):
return self.reference
and the form:
class ProcurementOperationRecord(forms.Form)
Id = forms.CharField(required=True)
Quantity = forms.FloatField(required=True)
NetAmount = forms.FloatField(required=True)
Supplier = forms.CharField(required=True)
SellPrice = forms.FloatField(required=True)
I have no clue how to let the user input the values in form and automatically add Quantity to StockOnOrder as well as automatically recognize the SupplierID based on Supplier. At this point I don't know where to start really. At least, is it possible to achieve what I try to do?
First, I've changed some things around and added some comments to what and why I did them.
# models/classes in python are singular AND camel cased (99.9%)
class Supplier(models.Model):
...
# models/classes in python are singular AND camel cased (99.9%)
class Replenishment(models.Model):
# attributes are normally lower case and snake cased (99.9%)
# try not to do this, a CharField??, unless you're using a guid? if so use UUIDField()
# https://docs.djangoproject.com/en/3.1/ref/models/fields/#uuidfield
id = models.CharField(db_column='Id', max_length=100, primary_key=True, verbose_name='references')
name = models.CharField(db_column='Name', max_length=200)
quantity = models.FloatField(db_column='Quantity', default=0)
net_amount = models.FloatField(db_column='NetAmount', default=0)
# deleted your field "Supplier" -- with this change you can join to the other table and get what you need without having to duplicate anything
supplier = models.ForeignKey(Supplier, db_column='SupplierID')
sell_price = models.DecimalField(db_column='SellPrice', default=0, max_digits=6, decimal_places=2) # You're asking for trouble if you keep this as FloatField
stock_on_order = models.IntegerField(db_column='StockOnOrder', default=0) # how can you have ordered a .5 for your stock? changed to IntegerField
stock_on_hand = models.IntegerField(db_column='StockOnHand', default=0) # how can you have a .5 of your stock? changed to IntegerField
class Meta:
db_table = 'replenishment' # try not to do this either.. let django come up with the name.. unless you're using an existing database/table?
...
# models/classes in python are singular AND camel cased (99.9%)
# django has a standard that they normally postfix forms with "Form" at the end of the class (no matter if it's a ModelForm or regular Form)
class ProcurementOperationRecordForm(forms.ModelForm)
class Meta:
model = Replenishment
fields = ('id', 'quantity', 'net_amount', 'supplier', 'sell_price')
# I would remove the "id", the client shouldn't care or know about it..
Now to create and update. (This would live inside a view)
# creating?
form = ProcurementOperationRecordForm(data=request.POST)
if form.is_valid():
form.save()
return redirect(..) or render(..)
# updating?
replenishment = Replenishment.objects.get(id='...something')
form = ProcurementOperationRecordForm(data=request.POST, instance=replenishment)
if form.is_valid():
form.save()
return redirect(..) or render(..)
This is just a general idea. You can try something like this.
First get the user input values of quantity and supplier like this from the valid form.
quantity = form.cleaned_data.get('quantity')
supplier = form.cleaned_data.get('supplier')
Then you can update your replenishment model
replenishment.objects.filter(Supplier=supplier).update(StockOnOrder=quantity)

Django count with group in annotation

I have a profile model with a ManyToMany field called that relates to a Stock. In my admin dashboard I'm trying to show the number of watchlists each stock is in. The annotation query I have is:
qs.annotate(watchlist_count=Count('profile__watchlist__symbol'))
But it's returning incorrect results
Here are the models:
class Profile(models.Model):
user = OneToOneField(User, on_delete=models.CASCADE)
watchlist = ManyToManyField(Stock, blank=True)
def __str__(self):
return self.user.email
class Stock(models.Model):
symbol = CharField(max_length=15, unique=True)
name = CharField(max_length=100)
category = CharField(max_length=30, choices=CATEGORY_CHOICES, blank=True)
about = TextField(help_text='About this company')
def __str__(self):
return f'{self.symbol} - {self.name}'
The equivalent SQL query is:
select stock_id, count(stock_id) from api_profile_watchlist group by stock_id;
What is wrong with my annotation query?
You do too much joins. By joining twice over the many-to-many relation, you "blow up" the count.
You can simply count the amount of watchlists with that Stock, with:
from django.db.models import Count
Stock.objects.annotate(
watchlist_count=Count('profile')
)
This works since, by default, the related_query_name=… [Django-doc] has the name of the model (or the related_name if you specified one). So the implicit relation you wrote from Stock to Profile (the reverse one of Profile to Stock in your watchlist relation), is profile (in lowercase). We thus ask Django to count, for a given Stock object, the number of relations to a Profile.

Fill form fields dynamically in django

It's a little example of my model:
class Food(models.Model):
food = models.CharField(max_length=50)
calories = models.FloatField()
class MenuFood(models.Model):
food = models.ForeignKey('Food')
amount = models.FloatField()
In django admin, when i call template MenuFood i get one ChoiceField with the foods, and TextInput for fill amount, i put extra field in form(TextInput Result).
How can i fill the extra field Result when user select some food or type in TextInput amount or whatever, multiply calories(class Food) * amount(MenuFood)?
Some idea, thanks.

Categories