Filtering Django models by user & object - django

I'm learning Django with a dummy example but having difficulty in understanding how to correctly filter my Django models by an authorised user in my views.
In my view I want to list the transactions associated with a users portfolio. The code below runs but when trying to access the result of 't' I get the error:
'ValueError: The QuerySet value for an exact lookup must be limited to one result using slicing.'
Any help would be much appreciated, thanks.
if request.user.is_authenticated:
# Get model data
pf = Portfolio.objects.filter(user=request.user)
t = Transaction.objects.filter(pf=pf)
My model is as below:
from django.db import models
from django.contrib.auth.models import User
class Portfolio(models.Model):
# Portfolio has one user associated with it
user = models.ForeignKey(User, on_delete=models.CASCADE)
name = models.CharField(max_length=100, default='-')
def __str__(self):
return self.name
class Transaction(models.Model):
# Transaction has one equity associated with it
equity = models.ForeignKey('Equity', on_delete=models.CASCADE, null=True)
# Transaction has one portfolio associated with it
pf = models.ForeignKey('Portfolio', on_delete=models.CASCADE)
BUY = 'BUY'
SELL = 'SELL'
BUY_OR_SELL = (
(BUY, 'BUY'),
(SELL, 'SELL'),
)
action = models.CharField(choices=BUY_OR_SELL, default=BUY, max_length=5)
num = models.FloatField(default=1)
price = models.FloatField(default=0)
date = models.DateField('date')
fee = models.FloatField(default=0)
def __str__(self):
return f'{self.equity}, {self.num}x{self.price}, {self.date:%d %b %Y}'
class Equity(models.Model):
class Meta:
verbose_name_plural = "Equities"
CUR_EUR = 'EUR'
CUR_GBP = 'GBP'
CUR_USD = 'USD'
CURRENCY_CHOICES = (
(CUR_EUR, 'EUR'),
(CUR_GBP, 'GBP'),
(CUR_USD, 'USD'),
)
symbol = models.CharField(max_length=20, default='-')
exchange = models.CharField(max_length=100, default='-')
currency = models.CharField(max_length=15, choices=CURRENCY_CHOICES, default=CUR_USD)
def __str__(self):
return self.symbol
Many thanks!

pf is here a collection of Portfolio objects, so you can query it with the __in lookup [Django-doc]:
Transaction.objects.filter(pf__in=pf)
Or if you are not interested in the Porfolio objects itself, you can make a query like:
Transaction.objects.filter(pf__user=request.user)
The query below will result in a query like:
SELECT transaction.*
FROM transaction
JOIN portfolio ON transaction.pf_id = portfolio.id
WHERE porfolio.user_id = 123
(with 123 the id of the request.user)

Related

Relationships in Django - how to filter data

I've looked at a lot of entries and I know how to filter simple relationships. Unfortunately, I'm stuck and I don't know how to filter my table data when one of the tables is a branch of a certain string.
models.py
from django.contrib.auth.models import User
class Autor(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE, unique=True)
description = models.TextField(blank=True, null=True)
class Incident(models.Model):
group_no = models.ForeignKey(Group, on_delete=models.CASCADE, default=1)
description = models.TextField(blank=True, null=True)
deleted = models.BooleanField(default=False)
class Department-leader(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="leader")
department = models.ForeignKey(Group, on_delete=models.CASCADE)
class Group(models.Model):
group_no = models.CharField(max_length=40, unique=True)
active = models.BooleanField(default=True)
views.py
def get_author(user):
qs = Autor.objects.filter(user=user)
if qs.exists():
return qs[0]
return None
def show_all(request):
show_all_records = Incident.objects.filter(deleted=False).order_by('-id')[:300]
if request.user.is_authenticated:
autors_list = get_author(request.user)
user_list = get_user_model()
logged_user = get_object_or_404(user_list, username__exact=autors_list)
(...)
print("Logged user: " + str(logged_user.id))
else:
logged_user = ""
context = {
'show_all_records': show_all_records,
'logged_user': logged_user,
}
return render(request, 'incident/all_records.html', context)
The show_all_records variable represents all the records of the Incident table and that is ok.
The second thing I would like to display are entries for the logged in person i.e. all incidents in particular departments of the leader who is logged in.
If the tables were connected linearly, I would have no problem building this filter.
But how to make a filter for this layout of tables?
In pure SQL, it would look something like this:
select
bledy_bledy.nr_zlecenia,
bledy_bledy.ilosc_bledow,
bledy_gruparobocza.nr_grupy,
auth_user.username,
auth_user.id
from
bledy_bledy
LEFT JOIN
bledy_lider_dzial
on
bledy_bledy.nr_grupy_roboczej_id = bledy_lider_dzial.dzial_id
LEFT JOIN
bledy_gruparobocza
on
bledy_lider_dzial.dzial_id = bledy_gruparobocza.id
LEFT JOIN
auth_user
on
bledy_lider_dzial.user_id = auth_user.id
where
auth_user.id = 4
**Can I count on some hint on how to build it?**
I think this may be what you want.
user = request.user
# only need the primary keys of the group leaders for the next query
department_ids = user.leader.all().values_list('department_id', flat=True)
incidents = Incident.objects.filter( group_no_id__in= departments )
Also a suggestion: stick to Django conventions for naming foreign keys and related names. leaders not leader (because it refers to a plurality or set of leaders), and group not group_no because it refers to a Group object. (The actual FK/primary key value is obtained by appending _id to the field name, as used in the above. This is Django "magic".)

Import-Export with "multilevel" models

I am figuring how can I manage this situation with django-import-export using the same excel and differents models with differents djangoapps. I have the following models:
# employees/models.py
class Employee(models.Model):
name = models.CharField(max_length=100)
job = models.ForeignKey(Job, on_delete=models.SET_NULL, null=True, blank=True)
# jobs/models.py
class Job(models.Model):
value = models.CharField(max_length=100)
department = models.ForeignKey(Department, on_delete=models.SET_NULL, null=True, blank=True)
place = models.ForeignKey(Place, on_delete=models.SET_NULL, null=True, blank=True)
class Department(models.Model):
value = models.CharField(max_length=100)
business = models.ForeignKey(Business, on_delete=models.SET_NULL, null=True, blank=True)
class Place(models.Model):
value = models.CharField(max_length=100)
business = models.ForeignKey(Business, on_delete=models.SET_NULL, null=True, blank=True)
class Business(models.Model):
name = models.CharField(max_length=100)
On excel I have following values:
xls_employee_name, xls_employee_job, xls_business_name
Jon Doe, Web Developer, Company 1
I know how to import employee name and his job because Job is directly related by ForeignKey. But how can I import business name?
Below is the code for employee name and his job:
# employees/resource.py
class EmpleadoResource(resources.ModelResource):
name = fields.Field(attribute='nombre', column_name='Nombre')
job = fields.Field(
column_name='xls_employee_job',
attribute='job',
widget=ForeignKeyWidget(
Job,
field='value'
))
class Meta:
model = Employee
fields = ('name','job',)
skip_unchanged = True
def before_import_row(self, row, **kwargs):
self.job = row["xls_employee_job"]
def after_import_instance(self, instance, new, row_number=None, **kwargs):
Job.objects.get_or_create(name=self.job)
Is it possible to import business name using one excel? I appreciate if someone could guide me. I'm also pretty new with django.
thank you for all your answers. I finally manage it and this is the solution(it was a little trickier for me, but very simple. I tried to translate all spanish names to english, sorry if I misslooked some):
# employees/resource.py
class EmployeeResource(resources.ModelResource):
name = fields.Field(attribute='name', column_name='Name')
job = fields.Field(
column_name='xls_employee_job',
attribute='job',
widget=ForeignKeyWidget(
Job,
field='value'
))
place = fields.Field(attribute='place', column_name='xls_place_column')
department = fields.Field(attribute='department', column_name='xls_department_column')
business = fields.Field(attribute='business', column_name='xls_business_name')
class Meta:
model = Employee
fields = ('name','job','place','department','business')
skip_unchanged = True
def before_import_row(self, row, **kwargs):
self.job = row["xls_employee_job"]
self.place = row["xls_place_column"]
self.department = row["xls_department_column"]
self.business = row["xls_business_name"]
def after_import_instance(self, instance, new, row_number=None, **kwargs):
Job.objects.get_or_create(name=self.job)
Centro.objects.get_or_create(name=self.centro)
Departamento.objects.get_or_create(name=self.departamento)
Empresa.objects.get_or_create(nombre=self.empresa)
I was stucked using widgets for business, department and place, but it was not necessary
Option 1
You can assign the column value for each row to a temporary attribute on the model (it doesn't matter that this attribute is not in the Employee model):
def before_import_row(self, row, **kwargs):
self.job = row["xls_employee_job"]
# assign row value to a temporary attr
self.business = row["xls_business_name"]
def after_import_instance(self, instance, new, row_number=None, **kwargs):
Job.objects.get_or_create(name=self.job)
# create instance using temp attr value
Business.objects.get_or_create(name=self.business)
Option 2
You can create all Business instances by processing the dataset in a batch before processing the Employee resources:
def before_import(self, dataset, using_transactions, dry_run, **kwargs):
for row in dataset.dict:
business = row["xls_business_name"]
Business.objects.create(name=business)
Note that you could use bulk_create() here to make this process more efficient.
Add transaction protection as you see fit, and bear in mind that the Business and Job entities will be created even if your import fails.

django_filters, how do I query based on associated model?

My problem is like so,
I am using django-tables2 and I want to list some people on my page but these people should go through some queries. These queries will change according to information from other models.if the query is ok, this person will be in my table.
# My models
class AgeGroup(models.Model):
age_group = models.CharField(choices=age_choices, max_length=5)
class SolvedExam(models.Model):
age = models.ForeignKey(AgeGroup, on_delete=models.CASCADE, related_name='solved_exam_age_group')
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='solved_exam')
class Person(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='person')
age = models.ForeignKey(AgeGroup, on_delete=models.CASCADE, related_name='person_age_group')
*
*
*
class Exam(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='person')
age = models.ForeignKey(AgeGroup, on_delete=models.CASCADE, related_name='exam_age_group')
*
*
*
# my view
class PersonList(SingleTableMixin, FilterView):
table_class = PersonTable
model = Person
queryset = Person.objects.all()
paginate_by = 10
template_name = 'person/person-list.html'
filterset_class = PersonFilter
def get_queryset(self):
super(Ogrenciler, self).get_queryset()
return Person.objects.filter( **some query** )
raise Http404
I want to list the students if there are exams which is not finished in the person's age group. Thank you very much!
Where is the information about the examns, and the examns that where not passed? Typically I would expect an fields like Exman_id, class name, boolean passed etc.

how to fetch data from many to many field objects?

I am getting stuck in many to many field objects in my model i want to retrieve the price of product from the order model objects but while fetching it gives sometime many related manager error or query set error
models.py
from django.db import models
from django.contrib import admin
from django.contrib.auth.models import User
class Product(models.Model):
name = models.CharField(max_length=100, db_index=True)
slug = models.SlugField(max_length=100, db_index=True)
description = models.TextField(blank=True)
price = models.DecimalField(max_digits=10, decimal_places=2)
available = models.BooleanField(default=True)
stock = models.PositiveIntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Order(models.Model):
user = models.ForeignKey(User,on_delete= models.SET_NULL,null = True)
product = models.ManyToManyField('Order.Product')
is_ordered = models.BooleanField(default =False)
date_ordered = models.DateTimeField(auto_now = True,null = True)
views.py
def get_product_price(request):
if request.method=="GET":
user=User.objects.get(username = "hemant")
orders = user.order_set.all()
order = orders[0]
price = order.product.price
return HttpResponse(price)
When you are getting value from ManyToManyField, you need to do it like this:
products = order.product.all()
If you want to get the prices, you can do it like this:
products = order.product.all()
for product in products:
print(product.price)
If you want to return prices as a http response, then you can use values_list() to get list of prices from queryset. Like this
import json
views.py
def get_product_price(request):
if request.method=="GET":
user=User.objects.get(username = "hemant")
orders = user.order_set.all()
order = orders[0]
price = list(order.product.all().values_list('price', flat=True))
return HttpResponse(json.dumps(price))

updating account balance with django

absolute n00b here, just fiddling around. Trying to make a very simple app to track my personal expenses. I have a class for entering the expenses, a class for the categories and a class for my account balance. Plan is to create en entry in the account balance everytime I create an expense.
How do I update my account balance? I'll have to get fields from the latest entry in expenses to do the math with in my balance class, right?
This is what I have. Any help appreciated.
from django.db import models
from django.utils import timezone
class Category(models.Model):
category = models.CharField(max_length=200,blank=False)
def __str__(self):
return self.category
class Balance(models.Model):
date = models.DateTimeField(default=timezone.now)
previous_balance = ????
transaction = ????
current_balance = previous_balance - transaction
class Expense(models.Model):
date = models.DateTimeField(default=timezone.now)
spender = models.ForeignKey('auth.User')
description = models.CharField(max_length=200)
category = models.ForeignKey(Category,default=1)
ABN = 'ABN'
ING = 'ING'
ACCOUNT_CHOICES = (
(ABN, 'ABN'),
(ING, 'ING'),
)
account = models.CharField(
max_length=30,
choices=ACCOUNT_CHOICES,
default=ABN,
)
amount = models.DecimalField(max_digits=10, decimal_places=2)
def commit(self):
self.commit_date = timezone.now()
self.save()
def __str__(self):
return u"%s. Kosten: %s" % (self.description, self.amount)
If I'm understanding your question correctly, you want to be able to get your current balance after creating Expenses. If so, you can use Django's aggregation:
from django.db.models import Sum
class Balance(models.Model):
date = models.DateTimeField(default=timezone.now)
# Keep the amount you start with
starting_balance = models.IntegerField()
# Get the Sum of all expenses and do some simple subtraction
def get_current_balance(self):
total_expenses = Expense.objects.all().aggregate(Sum('amount'))
return self.starting_balance - total_expenses['amount__sum']
Then in your views, you can do something like:
current_balance = some_balance_instance.get_current_balance()
considered balance change will be trigger by expense record change, you can overwrite save on Expense model. then balance table can be maintain in auto.
import datetime
class Expense(models.Model):
date = models.DateTimeField(default=timezone.now)
spender = models.ForeignKey('auth.User')
description = models.CharField(max_length=200)
category = models.ForeignKey(Category,default=1)
ABN = 'ABN'
ING = 'ING'
ACCOUNT_CHOICES = (
(ABN, 'ABN'),
(ING, 'ING'),
)
account = models.CharField(
max_length=30,
choices=ACCOUNT_CHOICES,
default=ABN,
)
amount = models.DecimalField(max_digits=10, decimal_places=2)
def save(self, *args, **kwargs):
super(Expense, self).save(*args, **kwargs)
last_bal = Balance.objects.order_by('id').last()
Balance.objects.create(date=datetime.datetime.now(), previouse_balance=last_bal.current_balance,
transaction=self, current_balance=last_bal.current_balance + self.amount)