Django: Build up portfolio valuation table with two queryset - django-views

I want build a portfolio position table based on those models (models.py):
class Portfolio(models.Model):
"""
Portfolio model, portfolio description
"""
id = models.UUIDField(primary_key=True,default=uuid.uuid4,editable=False)
code = models.CharField(max_length=100, blank=True)
class PortfolioPosition(models.Model):
"""
PortfolioPosition Model, instruments hold in portfolio
"""
portfolio = models.ForeignKey(Portfolio, on_delete=models.CASCADE, related_name='portfolio',null=True)
private_debt_issue = models.OneToOneField(PrivateDebtIssue, on_delete=models.CASCADE)
start_date = models.DateField(null=False, default=date.today)
end_date = models.DateField(null=True, blank=True)
quantity = models.DecimalField(max_digits=12, decimal_places=4, default=Decimal('1.0000'))
class PrivateDebtIssue(models.Model):
"""
Model representing a Private Debt instrument.
"""
id = models.UUIDField(primary_key=True,default=uuid.uuid4,editable=False)
name = models.CharField(max_length=250, blank=True)
class PrivateDebtIssueValuation(models.Model):
"""
DataPoint value
"""
private_debt_issue = models.ForeignKey(PrivateDebtIssue,on_delete=models.CASCADE, related_name='private_debt_issue')
date = models.DateField()
fund_value = models.DecimalField(max_digits=12, decimal_places=2, default=Decimal('0.00'))
Maybe is not the best way to handle it, but I want build a portfolio position table with all open private debt positions and the last value, based on a input date.
I wrote this code:
class PortfolioDetailView(DetailView):
model = Portfolio
context_object_name = 'portfolio'
template_name = 'private_debts/portfolio_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# INPUT DATE BASED ON DATEPICKER
# set default date value
input_date = date.today()
if self.request.method == 'GET':
valuation_date = self.request.GET.get('valuation_date')
if valuation_date is not None:
input_date = datetime.strptime(valuation_date,'%m/%d/%Y')
# based on Portfolio object get portfolio positions and related PrivateDebtIssue objects
context['portfolio_position'] = PortfolioPosition.objects.all().select_related('portfolio','private_debt_issue').filter(
Q(portfolio=self.object)
& Q(start_date__lte=input_date)
& (Q(end_date__isnull=True) | Q(end_date__gte=input_date))
)
context['position_value'] = queryset
return context
This code works fine, but how do I get the value of the PrivateDebtIssue for a specific date and put it in a table?
I want to build a table with portfolio PortfolioPosition .position, PrivateDebtIssue.Name and PrivateDebtIssueValuation.fund_value, based on a valuation date.

At the end, I did something not recommended in Django: direct query with execute function. Like this
class PortfolioDetailView(DetailView):
model = Portfolio
context_object_name = 'portfolio'
template_name = 'private_debts/portfolio_detail.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# set default date value
input_date = date.today()
input_date = date(2019,12,31)
if self.request.method == 'GET':
valuation_date = self.request.GET.get('valuation_date')
if valuation_date is not None:
input_date = datetime.strptime(valuation_date,'%m/%d/%Y')
sql = """
SELECT * \
FROM private_debts_portfolioposition \
INNER JOIN private_debts_privatedebtissue \
ON (private_debts_portfolioposition.private_debt_issue_id = private_debts_privatedebtissue.id) \
INNER JOIN private_debts_privatedebtissuevaluation \
ON (private_debts_privatedebtissuevaluation.private_debt_issue_id = private_debts_privatedebtissue.id) \
WHERE private_debts_portfolioposition.portfolio_id = '{}'::uuid \
AND private_debts_privatedebtissuevaluation.date = '{}'
""".format(self.object.id, input_date)
portfolio_position = my_custom_sql(sql)
context['portfolio_position_table'] = portfolio_position
context['valuation_date'] = valuation_date
return context
And I used the example 'suggested' by the docs
These are the two functions:
def dictfetchall(cursor):
"Return all rows from a cursor as a dict"
columns = [col[0] for col in cursor.description]
return [
dict(zip(columns, row))
for row in cursor.fetchall()
]
def my_custom_sql(sql):
with connection.cursor() as cursor:
cursor.execute(sql)
row = dictfetchall(cursor)
return row
I hope someone could suggest a better way to handle it.
Thanks.

Related

Querying other Model in class-based view produces error

I have two tables, in one of which the possible items with their properties are recorded, in the other the stock levels of these respective items are recorded.
class itemtype(models.Model):
item_id = models.IntegerField(primary_key=True)
item_name = models.CharField(max_length=100)
group_name = models.CharField(max_length=100)
category_name = models.CharField(max_length=100)
mass = models.FloatField()
volume = models.FloatField()
packaged_volume = models.FloatField(null=True)
used_in_storage = models.BooleanField(default=False, null=True)
class Meta:
indexes = [
models.Index(fields=['item_id'])
]
def __str__(self):
return '{}, {}'.format(self.item_id, self.item_name)
class material_storage(models.Model):
storage_id = models.AutoField(primary_key=True)
material = models.ForeignKey(itemtype, on_delete=models.PROTECT)
amount_total = models.IntegerField(null=True)
price_avg = models.FloatField(null=True)
amount = models.IntegerField(null=True)
price = models.FloatField(null=True)
timestamp = models.DateTimeField(default=timezone.now)
def __str__(self):
return '{}, {} avg.: {} ISK'.format(self.material, self.amount, self.price)
I have a ModelForm based on the table material_storage, in which a checkbox indicates whether transport costs should be included or not.
In the form_valid() method of this ModelForm class the calculations are performed. To do so, I have to retrieve the volume per unit of the given item to use it for my transport cost calculations. Trying to geht that value the way shown below leads to an error I don't really understand.
class MaterialChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return obj.item_name
class NewAssetForm(forms.ModelForm):
material = MaterialChoiceField(models.itemtype.objects.filter(used_in_storage= True))
needs_transport = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
super(NewAssetForm, self).__init__(*args, **kwargs)
self.fields['amount'].widget.attrs['min'] = 1
self.fields['price'].widget.attrs['min'] = 0.00
class Meta:
model = models.material_storage
fields = (
'material',
'amount',
'price',
)
widgets = {
'material': forms.Select(),
}
class NewItemView(FormView):
template_name = 'assetmanager/newasset.html'
form_class = forms.NewAssetForm
success_url = '/storage/current'
def form_valid(self, form):
unit_volume = itemtype.objects.values('packaged_volume').filter(item_id=form.cleaned_data['material'])[0]['packaged_volume']
I believe that this has something to do with querying a different model than specified in the form, but I don't understand what exactly is the problem. Especially the fact, that running the exact same query in the django shell returns the correct value does not really help to understand what is going wrong here. Could somebody please tell me how to get the desired value the correct way?
Change last line from:
unit_volume = itemtype.objects.values('packaged_volume').filter(item_id=form.cleaned_data['material'])[0]['packaged_volume']
to:
unit_volume = itemtype.objects.values('packaged_volume').filter(item_id=form.cleaned_data['material'].item_id)[0]['packaged_volume']
The error says, you are giving Item instance to the query, where is item_id asked.

Django tables2 TypeError: Object of type Client is not JSON serializable

I'm trying to implement a big data on django-tables2, but with bootstrap table fitches (sort, filter, export, click to select, etc).
Found a solution but it works with only simple fields, in my project, some fields in the model are linked to another model:
class Cars(models.Model):
dealer = models.ForeignKey(Dealer, blank=True, null=True, on_delete=models.CASCADE)
slug = models.SlugField(null=True, unique=True)
VIN = models.CharField(max_length=19, blank=True, null=True)
model = models.CharField(max_length=50) # Car_model, on_delete=models.CASCADE
client = models.ForeignKey(Client, blank=True, null=True, on_delete=models.CASCADE)
manager = models.ForeignKey(Manager, blank=True, null=True, on_delete=models.CASCADE)
This is my code in views.py:
class TableViewMixin(SingleTableMixin):
# disable pagination to retrieve all data
table_pagination = False
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
# build list of columns and convert it to an
# ordered dict to retain ordering of columns
# the dict maps from column name to its header (verbose name)
table: Table = self.get_table()
table_columns: List[Column] = [
column
for column in table.columns
]
# retain ordering of columns
columns_tuples = [(column.name, column.header) for column in table_columns]
columns: OrderedDict[str, str] = OrderedDict(columns_tuples)
context['columns'] = columns
return context
def get(self, request, *args, **kwargs):
# trigger filtering to update the resulting queryset
# needed in case of additional filtering being done
response = super().get(self, request, *args, **kwargs)
if 'json' in request.GET:
table: Table = self.get_table()
data = [
{column.name: cell for column, cell in row.items()}
for row in table.paginated_rows
]
return JsonResponse(data, safe=False)
else:
return response
class CarsTableViewMixin(TableViewMixin, ListView):
template_name = 'modules/cars_tables3.html'
table_class = CarsTable
queryset = Cars.objects.all()
I have an error: TypeError: Object of type Client is not JSON serializable
"GET /table3/?json&searchText= HTTP/1.1" 500 146887
I know that there is something to add in serialising my json string, but I'm not yet good at it.
Have you some suggestions on it please?

Django query include join

I tried to calculate the count of the increased and decreased competitor product price which are related to products. I couldn't handle it. Can I do this by overriding get_context_data method? I couldn't write a query to get the data from comp_product model. How can I handle this?
Models:
class Product(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE,related_name='products')
category = models.CharField(max_length=120)
brand = models.CharField(max_length=120)
product = models.CharField(max_length=120)
price = models.DecimalField(decimal_places=2,max_digits=100)
class Comp_Product(models.Model):
product = models.ForeignKey(Product,on_delete=models.CASCADE, related_name="comp_products")
competitor = models.URLField()
price = models.DecimalField(decimal_places=2,max_digits=100)
change = models.FloatField()
stock = models.BooleanField()
last_update = models.DateField(auto_now_add=True)
View:
class DashboardList(ListView):
template_name='dashboard_by_user.html'
def get_queryset(self):
p = Product.objects.filter(user=self.request.user)
return p
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
Products = context['object_list']
context['distinct_category_count'] = Products.values('category').distinct().count()
context['distinct_brand_count'] = Products.values('brand').distinct().count()
context['product_count'] = Products.values('product').count()
context['num_comp_products_with_lower_price'] = p.comp_products.filter(price__lt=p.price).count()
context['num_comp_products_with_higher_price'] = p.comp_products.filter(price__gt=p.price).count()
return context
If I assume you want to retrieve for a Product p the number of competitor products Comp_Product which have a lower or higher price then this will get you started:
# This is the product you're looking at, it'll be retrieved
# via the queryset and it's just randomly the first.
# If you want to do this for all products in the queryset
# you'll need to work with annotate.
p = self.queryset.first()
num_comp_products_with_lower_price = p.comp_products.filter(price__lt=p.price).count()
num_comp_products_with_higher_price = p.comp_products.filter(price__gt=p.price).count()
If you have p in your view it shouldn't be a problem to do this in get_context_data.

How to add a calculation in Django model?

I just want to be Per Amount to Paid Amount and result will be posted in balance automatic
this is my admin.py
#admin.register(StudentPaymentSchedules)
class StudentPaymentSchedulesAdmin(admin.ModelAdmin):
list_display = ('Fullname','Grade_Level','Payment_Schedule','Amount','Student_Payment_Date','Paid_Amount','Balance','Remarks',)
ordering = ('pk','Students_Enrollment_Records__Education_Levels')
search_fields = ('Students_Enrollment_Records__Student_Users__Firstname','Students_Enrollment_Records__Student_Users__Lastname',)
list_filter = ('Students_Enrollment_Records__Education_Levels',)
def Fullname(self, obj):
return obj.Students_Enrollment_Records.Student_Users
def Grade_Level(self, obj):
return obj.Students_Enrollment_Records.Education_Levels
actions = ["export_as_csv"]
def export_as_csv(self, request, queryset):
meta = self.model._meta
field_names = [field.name for field in meta.fields]
response = HttpResponse(content_type='text/csv')
response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
writer = csv.writer(response)
writer.writerow(field_names)
for obj in queryset:
row = writer.writerow([getattr(obj, field) for field in field_names])
return response
I have this models.py
class StudentPaymentSchedules(models.Model):
Students_Enrollment_Records=models.ForeignKey(StudentsEnrollmentRecord, related_name='+', on_delete=models.CASCADE,null=True,blank=True)
Payment_Schedule =models.CharField(max_length=500,null=True,blank=True)
Amount = models.FloatField(null=True,blank=True)
Student_Payment_Date = models.DateField(null=True, blank=True)
Paid_Amount = models.FloatField(null=True,blank=True)
Balance = models.FloatField(null=True,blank=True)
Remarks=models.TextField(max_length=500,null=True,blank=True)
for example if the Amount is 1,950 and the Paid Amount is 1500 if the admin click the Save button it will cocompute.
you can use pre-save signals to do it.
from django.db.models.signals import pre_save
def balance_reciever(sender, instance, *args, **kwargs):
Amount = instance.Amount
Paid_Amount = instance.Paid_Amount
Balance = Amount-Paid_Amount
instance.Balance = Balance
pre_save.connect(balance_reciever, sender=StudentPaymentSchedules)
You can use Django Admin save_model but I think you shouldn't create a separate column for a division result. Instead you should use #property function in your model class like this:
class StudentPaymentSchedules(models.Model):
Students_Enrollment_Records=models.ForeignKey(StudentsEnrollmentRecord, related_name='+', on_delete=models.CASCADE,null=True,blank=True)
Payment_Schedule =models.CharField(max_length=500,null=True,blank=True)
Amount = models.FloatField(null=True,blank=True)
Student_Payment_Date = models.DateField(null=True, blank=True)
Paid_Amount = models.FloatField(null=True,blank=True)
Remarks=models.TextField(max_length=500,null=True,blank=True
#property
def balance(self):
return self.Amount - self.Paid_Amount
And use SQL divide function if you need to get data directly from database.

Django admin does not show all entities

I've inherited an app created with Django. There is a problem with it: in admin interface, the page lists not all entities (videos), but some (16 of 25). I have no idea, what is this.
Then I run python manage.py shell, and there Video.objects.all(), there are all 25 objects (counted them using len and by iterating them with for loop).
I have found no managers or whatever (maybe I just don't know where to look for them).
On the bottom of admin page: 25 videos, while there are only 16 rows.
Then I add to VideoModelAdmin class list_per_page = 10, paginator show three pages, but only first two of them has any Videos, third shows no rows.
Here are some code.
# admin.py
class VideoModelAdmin(admin.ModelAdmin):
list_display = ['title', 'short_desc', 'author', 'redactor_choise', 'views_num', 'rating', 'is_published']
list_filter = ['is_published', 'redactor_choise']
list_per_page = 10
actions = ['make_published', 'update_comments_count']
exclude = ('file_lq', 'file_hq', )#'thumb',)
def make_published(self, request, queryset):
queryset.update(is_published=1)
make_published.short_description = "Опубликовать выделенные"
def save_model(self, request, obj, form, change):
instance = form.save(commit=False)
instance.author = request.user
instance.save()
return instance
def update_comments_count(self, request, queryset):
for video in queryset:
video.update_comments_count()
update_comments_count.short_description = "Пересчитать комментарии!"
# later there
admin.site.register(Video, VideoModelAdmin)
# models.py
class Video(models.Model):
def make_upload_path(instance, filename):
return 'video/thumbs/' + generate_random_name(filename)
category = models.ManyToManyField(Category, limit_choices_to = {'is_published': 1})
title = models.CharField(max_length=128)
short_desc = models.CharField(max_length=255)
long_desc = tinymce_models.HTMLField(blank=True)
file_lq = models.FileField(upload_to='video/lq/', null=True, blank=True)
file_hq = models.FileField(upload_to='video/hq/', null=True, blank=True)
thumb = models.FileField(upload_to=make_upload_path, blank=True, null=True)
#thumb = fields.ThumbnailField(upload_to=make_upload_path, sizes=settings.VIDEO_THUMB_SIZE, blank=True, null=True)
author = models.ForeignKey(User, editable=False)
redactor_choise = models.BooleanField(default=False)
views_num = models.SmallIntegerField(default=0, editable=False)
comments_num = models.SmallIntegerField(default=0, editable=False)
rating = models.SmallIntegerField(default=0, editable=False)
voters = fields.PickledObjectField(blank=True, editable=False)
created = models.DateTimeField(auto_now_add=True)
is_published = models.BooleanField(default=False)
def get_absolute_url(self):
return "/video/%d" % self.id
def views_num_plus(self):
cursor = connection.cursor()
cursor.execute('update soctv_video set views_num=views_num+1 where id=%d', [self.id])
cursor.close()
def update_comments_count(self):
from threadedcomments.models import ThreadedComment as Comment
self.comments_num = Comment.objects.filter(video=self).count()
self.save()
#cursor = connection.cursor()
#cursor.execute('update soctv_video set comments_num = (select count(*) from soctv_comment where video_id = %s) where id = %s', [self.id, self.id])
#cursor.close()
def update_categories_counts(self):
cursor = connection.cursor()
cursor.execute('update soctv_category set num_items = (select count(*) from soctv_video_category where category_id = soctv_category.id)')
cursor.close()
def is_user_voted(self, uid):
try:
if self.voters[uid]:
return self.voters[uid]
except Exception:
return False
def increment_view_count(self, token):
import md5
token = md5.new(token).hexdigest()
if VideoView.objects.filter(uniquetoken=token).count() == 0:
VideoView(uniquetoken = token, video = self).save()
def view_count(self):
return self.views_num + VideoView.objects.filter(video=self).count()
def __unicode__(self):
return unicode(self.title)
The problem can be that some FK in some of your videos points to something that does not exist.
I had the same problem and this was the reason.
Django will silently fail if the value is not there in the foreign key column.
Add both null and blank attribute to the column
null=True, blank=True
Make sure that you are logged in to the correct account aswell.
In my case, My account did not have permissions to modify <Model>