How to insert multiple foreign key values in django? - django

i've tried using bulk_create but i couldn't insert the foreign key value, i want to add 100 bikes with about 20 stations
class Bike(models.Model):
ID_Bike = models.CharField(primary_key=True, max_length=120)
Belong_Station = models.ForeignKey(
Station, on_delete=models.CASCADE, related_name='listBike')
def __str__(self):
return self.ID_Bike
class Station(models.Model):
name_Station = models.CharField(max_length=120)
latitude = models.FloatField(max_length=120)
longitude = models.FloatField(max_length=120)
address = models.CharField(max_length=120, default="")
def __str__(self):
return self.name_Station

You should check out fixtures. If I were you, I would stick with the SQL though. This is how an insert statement that will insert two Station and three Bike records could look like.
insert into Station (id, name_Station, latitude, longitude, address) values (1, "Johns Garage", 41.12, 23.58, "Fast Road 25, 001 12 Wales") , (2, "Mike riders place", 42.56, 21.43, "Somewhere street 12");
insert into Bike (ID_Bike, Belong_Station) values ("Johns bike", 1), ("Johns second bike", 1), ("Mikes bike", 2);
Notice that you must first insert Station records. Otherwise the foreign key constraint will fail if you try to insert Bikes first.

Related

Django: How would I create a secondary ID field that is grouped (ranked) by a given foreign key?

The output could look like this, for example:
id
secondary_id
fk
1
1
1
2
2
1
3
3
1
4
1
2
5
2
2
For context:
(see models below)
I have a commission structure which will have brackets depending on how much a user is earning in a month.
Ideally, I need to know in my Commission Bracket model, the bracket index for a given structure.
Here are my models.
class CommissionStructure(APIBaseModel):
advisor = models.ManyToManyField(AdviserDetail)
name = models.CharField(max_length=20, blank=True, null=True, default='default')
company = models.ForeignKey(Company, on_delete=models.CASCADE)
start_dt = models.DateTimeField(auto_now_add=True)
end_dt = models.DateTimeField(default=timezone.datetime.max)
objects = CommissionStructureManager()
class CommissionBracket(APIBaseModel):
<secondary_id ???>
commission_structure = models.ForeignKey(CommissionStructure, on_delete=models.CASCADE, related_name="brackets")
lower_bound = models.DecimalField(decimal_places=2, default=0.00, max_digits=20, null=True, blank=True)
upper_bound = models.DecimalField(decimal_places=2, default=0.00, max_digits=20, null=True, blank=True)
Please note, I may not have to store it on my model if I can add an annotation to an aggregate set, but my preference is to follow DRY.
Thank you
My suggestion would be to execute custom SQL directly. You can add the secondary id as an integer field in CommissionBracket. Then, you can implement this:
from django.db import connection
def sample_view(request):
...
with connection.cursor() as cursor:
cursor.execute('''
INSERT INTO appname_commissionbracket (
secondary_id,
commission_structure_id
)
SELECT CASE
WHEN MAX(secondary_id)
THEN MAX(secondary_id) + 1
ELSE 1
END AS new_secid, %s
FROM appname_commissionbracket
WHERE commission_structure_id = %s''',
[1, 1] # Sample foreign key value
)
return render(...)
Here we're using INSERT INTO SELECT since we're basing the new record's secondary_id from the same table. We're also adding a CASE so that we can have a fallback value if no record with commission_structure_id value as 1 is returned.
In case you need to populate other columns during create, you can simply include them like so:
INSERT INTO (secondary_id, commission_structure_id, lower_bound, upper_bound)
SELECT CASE ... END AS new_secid, <fk_value>, <lower_bound_value>, <upper_bound_value>
I've found a way to annotate the queryset, but for interest, my original question still remains: how do I add another field partitioned by the foreign key?
brackets = CommissionBracket.objects.select_related("commission_structure")\
.prefetch_related(
'commission_structure__advisor',
'commission_structure__start_dt__gte',
'commission_structure__end_dt__lte',
'commission_structure__company',
'bracket_values'
).filter(
commission_structure__advisor=advisor,
commission_structure__start_dt__lte=date,
commission_structure__end_dt__gte=date,
commission_structure__company=advisor.user.company,
).annotate(index=Window(
expression=Count('id'),
partition_by="commission_structure",
order_by=F("lower_bound").asc()))

Django how to filter a foreign key object only with specific value

I have two tables.
Table Product
id | name |
Table Discount
id | product_id | is_deleted
Two models are:
class Product(models.Model):
name = models.CharField(max_length=10)
class Discount(models.Modle):
product = models.ForeignKey(Product, on_delete=models.CASCADE)
is_deleted = models.BooleanField(default=True)
product_id is the foreign key of product table's id, and is_deleted is a boolean field.
How can I filter all products only with is_deleted discount? Notice those two tables may be large, so .exclude() is not fit for this case.
product_ids_subquery = Discount.objects.values('product').annotate(
deleted=(Count('product') - Count('product', filter=Q(is_deleted=True)))
).filter(deleted=0).values('product')
products_qs = Product.objects.filter(id__in=product_ids_subquery)
we can identify that products only with is_deleted discount with the help of count after group by product. For each product,
calculate how many records are present in discount table. let say total_count = Count('product')
calculate how many records are present which is deleted. let say deleted_count = Count('product', filter=Q(is_deleted=True))
deleted = (total_count - deleted_count) gives you a no. which is not deleted
if deleted == 0 then you can say all mapped records are deleted
products = Product.objects.filter(discount__is_deleted=True)

Django advanced join / query, how to filter foreign keys?

I have this two models.
class City(models.Model):
city = models.CharField(max_length=200)
country = models.CharField(max_length=200)
class CityTranslation(models.Model):
city = models.ForeignKey(City)
name = models.CharField(max_length=200)
lang = models.CharField(max_length=2)
prio = models.IntegerField()
Every city can have multiple translated names within one language.
So I want to get all City's with country="Poland". If a corresponding City has one or more CityTranslations with lang=name. I want to get only the first ordered by prio.
I am doing something like that now.
City.objects.filter(country="Poland", citytranslation__land="pl").annotate(transname=F("alt_names__name"))
But this is not working, because:
If there is a City without a CityTranslation it want be listed
If there are multiple CityTranslation's they all will be shown. But I just want the first. (... .ordered_by('prio').first())
Any idea?
EDIT:
Solved it by using a #property field, which is ordering my CityTranslation by prio and picks the first one:
#propert
def transcity(self):
return self.citytranslation.filter(lang="pl").order_by('-prio').first()
def magic(passed_country="Poland", passed_lang="pl")
# I want to get all City's with country="Poland".
cities = City.objects.filter(country=passed_country)
# If a corresponding City has one or more CityTranslations with lang=name. I want to get only the first ordered by prio.
suitable_cities = cities.filter(citytranslation__lang=passed_lang)
if suitable_cities.exists()
first_matching_city = suitable_cities.orderby('prio').first()
else:
first_matching_city = cities.orderby('prio').first()
return first_matching_city
May need to set up a relatedname on citytranslation.
May not need orderby if you plan on ordering by ID anyways.

Django combine filter on two fields

I am relatively new to Django. I'm having problem when filtering data. I have two models, given below:
Account(models.Model):
name = models.CharField(max_length=60)
hotel = models.ForeignKey(Hotel)
account_type = models.CharField(choices=ACCOUNT_TYPE, max_length=30)
Transaction(models.Model):
account = models.ForeignKey(Account, related_name='transaction')
transaction_type = models.CharField(choices=TRANSACTION_TYPE, max_length=15)
description = models.CharField(max_length=100, blank=True, null=True)
date_created = models.DateTimeField(default=timezone.now)
ACCOUT_TYPE is:
ACCOUNT_TYPE = (
(0, 'Asset'),
(1, 'Liabilities'),
(2, 'Equity'),
(3, 'Income'),
(4, 'Expense')
)
I want to filter all the transactions where the account type is Income and Expense within a given date range. How can I combine those filters in Django?
I have tried like this:
income_account = Account.objects.filter(account_type=3)
expense_account = Account.objects.filter(account_type=4)
transactions = Transaction.objects.filter(Q(
account=income_account,
date_created__gte=request.data['start_date'],
date_created__lte=request.data['end_date']
) & Q(
account=expense_account,
date_created__gte=request.data['start_date'],
date_created__lte=request.data['end_date'])).order_by('date_created')
But it's not working. It raises the following error:
ProgrammingError: more than one row returned by a subquery used as an expression
income_account and expense_account is not single object, it is a list of objects. So instead of this account=income_account and this account=expense_account try to use in: account__in=income_account and account__in=expense_account.
Also you probably could simplify queryset like this:
accounts = Account.objects.filter(Q(account_type=3) | Q(account_type=4))
transactions = Transaction.objects.filter(
account__in=accounts,
date_created__gte=request.data['start_date'],
date_created__lte=request.data['end_date']
).order_by('date_created')
Instead of having multiple querysets, you can have only one, as Q allows ORing of filters. You could do:
Transaction.objects.filter(
(Q(account__account_type=3) | Q(account__account_type=4)) &
Q(date_created__range=[start_date, end_date])
)
The __range can be used to get dates between the specified start_date and end_date.
You can always use in to lookup records by multiple values. So, if you want Transaction where ACCOUNT_TYPE are Income, Expenseyou can use it like this.
Transaction.objects.filter(Q(account__in=[3,4]) & Q(date_created__gte=request.data['start_date']) & Q(date_created__lte=request.data['end_date'])).order_by('date_created')
This will work for you:-
result = Account.objects.filter((account_type__in['Income','Expense'])
OR
result = Account.objects.filter((account_type__in['0','4'])
I have put 0 and 4 as string because you have mention account_type as CharField.

Django Query: get frequency of related objects by name

I have a list of rating objects, where every rating is associated with a studyspace
I'm trying to write a Query to return a breakdown of the number of votes for each given StudySpace
e.g.
Maths Room: 10
Physics Room: 2
Art Room: 9
I've tried and come up with the following:
studyspace_rating_breakdown = ratings.values('studyspace').annotate(num_votes=Count('studyspace')).order_by('-num_votes')[:3]
However this returns the id of the studyspace.
[{'studyspace': 8, 'num_votes': 421}, {'studyspace': 7, 'num_votes': 91}, {'studyspace': 2, 'num_votes': 2}]
Is there a way I can modify the query to return the studyspace name field instead of the ID?
I don't want to have to write a templatetag to look up the studyspace name from the ID within the template
The models themselves:
class StudySpace(models.Model):
department = models.ForeignKey(Department, on_delete=models.CASCADE)
space_name = models.CharField(max_length=200, unique=True)
...
class Rating(models.Model):
studyspace = models.ForeignKey(StudySpace, on_delete=models.CASCADE)
student = models.ForeignKey(Student, on_delete=models.CASCADE)
rating = models.IntegerField(default=3)
...
Just try to put up the answer in comment section, you could easily do:
ratings.values('studyspace', 'studyspace__space_name')
to show the related fields. For more info, check django query api for values.