Django: Appending certain values after updating model - python

I was wondering how I could make this work.
I'm currently doing this Django project that creates and modifies Client models, as such:
class Client(models.Model):
name = models.CharField(max_length=300)
birth_date = models.DateTimeField('birth date')
gender = models.CharField(max_length=1, choices=GENDER, null=True)
phone_number = models.CharField(max_length=13, null=True)
email = models.EmailField(max_length=254, null=True)
medical_condition = models.CharField(max_length=300, null=True)
experience = models.CharField(max_length=3, choices=EXPERIENCE, blank=True)
next_class = models.DateTimeField('next class')
Now, everything is pretty much working fine. I'm currently using generic form views to create and update students, so that's fine. However, I'm planning on doing a View where I can see a financial report of all the students who have taken classes, payments included. How would I go along with this? I could try somehow appending next_class to a dictionary of some sort each time I change its value, but I feel that's not efficient at all.
I've tried to see how I can do this; I was mostly thinking of using Django's Admin history app and integrating it to my actual webpage, but I found out about Simple History which seems pretty good. I feel like it doesn't exactly do the queries I want, though.
Thanks in advance, and I apologize if it's vague in any way. I can update my post with more code if necessary, just didn't want to make this question much longer.

I think your model is wrong. It seems like you need a separate related model for the classes that a student takes. It could be as simple as:
class Class(models.Model):
client = models.ForeignKey('Client')
date = models.DateTimeField()
so that instead of changing a field in the Client model, you add a new row to the Class model. Then it will be fairly simple to query all classes for a client between two dates:
classes_taken = my_client.class_set.filter(date_range=[start_date, end_date])
where my_client is an instance of Client, and start_date and end_date are the relevant dates.

Related

How to make (if & else) for internal values in django orm?

I am making models for my store project and I wanted to know why the code I wrote is wrong? And how can I write correctly?
I want only the time when a product is sold from me to be recorded in the database.
class Product(models.Model):
product_type = models.ForeignKey(ProductType, on_delete=models.PROTECT, related_name='products_types')
upc = models.BigIntegerField(unique=True)
title = models.CharField(max_length=32)
description = models.TextField(blank=True)
category = models.ForeignKey(Category, on_delete=models.PROTECT, related_name='products')
brand = models.ForeignKey(Brand, on_delete=models.PROTECT, related_name='products')
soled = models.BooleanField(default=False)
if soled == True:
soled_time = models.DateTimeField(auto_now_add=True)
created_time = models.DateTimeField(auto_now_add=True)
modified_time = models.DateTimeField(auto_now=True)
def __str__(self):
return self.title
I hope that my problem will solve the question of many friends <3
welcome to SO. Great question, let me explain.
Your model is correct, aside from the soled_time field, and the if statement above it.
That being said, I think you may be missing some logic here, unless you have some kind of single item single sale thing going on (as opposed to a stock of items) then you may need to add another model.
The product tends to be its own entity in this kind of example and we would create supporting models with relationships to the Product model.
For example, you may have a Cart model, which looks something like:
class Cart(models.Model):
products = models.ManyToManyField(Product, related_name='carts_in', on_delete=models.CASCADE)
# add fk to user, so we know who ordered
# add some functions which sum the total cost of products
I hope this kind of makes sense, and then we would move the soled_time field away from products and onto Cart, because then we know when cart was sold, which items were in the cart, and which user made the cart.
Please also consider looking into existing diango packages which manage A LOT of the heavy lifting when it comes to e-commerce, I would Google and sniff around a bit and see if any of the existing packages suit your needs first, most of them are quite extendable aswell so you should be able to make it work for your use case even if it doesn't fit right out of the box.
If you have any questions or I can add clarity to my answer, please feel free to add a comment, or if this satisfies your question, then dont forget to accept this answer and leave an upvote if you like.
As for the question of "How do I update the field when the item is sold?"
The answer is you override the save function of the model, like so:
class Cart(models.Model):
products = models.ManyToManyField(Product, related_name='carts_in', on_delete=models.CASCADE)
soled_time = models.DatetimeField()
# add fk to user, so we know who ordered
# add some functions which sum the total cost of products
def save(self, *args, **kwargs):
super().save(*args, **kwargs)
if self.sold:
self.sold_time = timezone.now()

Limit prefetch_related to 1 by a certain criteria

So I have models like these
class Status(models.Mode):
name = models.CharField(max_length=255, choices=StatusName.choices, unique=True)
class Case(models.Model):
# has some fields
class CaseStatus(models.Model):
case = models.ForeignKey("cases.Case", on_delete=models.CASCADE, related_name="case_statuses")
status = models.ForeignKey("cases.Status", on_delete=models.CASCADE, related_name="case_statuses")
created = models.DateTimeField(auto_now_add=True)
I need to filter the cases on the basis of the status of their case-status but the catch is only the latest case-status should be taken into account.
To get Case objects based on all the case-statuses, this query works:
Case.objects.filter(case_statuses__status=status_name)
But I need to get the Case objects such that only their latest case_status object (descending created) is taken into account. Something like this is what I am looking for:
Case.objects.filter(case_statuses__order_by_created_first__status=status_name)
I have tried Prefetch as well but doesnt seem to work with my use-case
sub_query = CaseStatus.objects.filter(
id=CaseStatus.objects.select_related('case').order_by('-created').first().id)
Case.objects.prefetch_related(Prefetch('case_statuses', queryset=sub_query)).filter(
case_statuses__status=status_name)
This would be easy to solve in raw postgres by using limit 1. But not sure how can I make this work in Django ORM.
You can annotate your cases with their last status, and then filter on that status to be what you want.
from django.db.models import OuterRef
status_qs = CaseStatus.objects.filter(case=OuterRef('pk')).order_by('-created').values('status__name')[:1]
Case.objects.annotate(last_status=status_qs).filter(last_status=status_name)

Django ORM really slow iterating over QuerySet

I am working on a new project and had to build an outline of a few pages really quick.
I imported a catalogue of 280k products that I want to search through. I opted for Whoosh and Haystack to provide search, as I am using them on a previous project.
I added definitions for the indexing and kicked off that process. However, it seems that Django is really, really really slow to iterate over the QuerySet.
Initially, I thought the indexing was taking more than 24 hours - which seemed ridiculous, so I tested a few other things. I can now confirm that it would take many hours to iterate over the QuerySet.
Maybe there's something I'm not used to in Django 2.2? I previously used 1.11 but thought I use a newer version now.
The model I'm trying to iterate over:
class SupplierSkus(models.Model):
sku = models.CharField(max_length=20)
link = models.CharField(max_length=4096)
price = models.FloatField()
last_updated = models.DateTimeField("Date Updated", null=True, auto_now=True)
status = models.ForeignKey(Status, on_delete=models.PROTECT, default=1)
category = models.CharField(max_length=1024)
family = models.CharField(max_length=20)
family_desc = models.TextField(null=True)
family_name = models.CharField(max_length=250)
product_name = models.CharField(max_length=250)
was_price = models.FloatField(null=True)
vat_rate = models.FloatField(null=True)
lead_from = models.IntegerField(null=True)
lead_to = models.IntegerField(null=True)
deliv_cost = models.FloatField(null=True)
prod_desc = models.TextField(null=True)
attributes = models.TextField(null=True)
brand = models.TextField(null=True)
mpn = models.CharField(max_length=50, null=True)
ean = models.CharField(max_length=15, null=True)
supplier = models.ForeignKey(Suppliers, on_delete=models.PROTECT)
and, as I mentioned, there are roughly 280k lines in that table.
When I do something simple as:
from products.models import SupplierSkus
sku_list = SupplierSkus.objects.all()
len(sku_list)
The process will quickly suck up most CPU power and does not finish. Likewise, I cannot iterate over it:
for i in sku_list:
print(i.sku)
Will also just take hours and not print a single line. However, I can iterate over it using:
for i in sku_list.iterator():
print(i.sku)
That doesn't help me very much, as I still need to do the indexing via Haystack and I believe that the issues are related.
This wasn't the case with some earlier projects I've worked with. Even a much more sizeable list (3-5m lines) would be iterated over quite quickly. A query for list length will take a moment, but return the result in seconds rather than hours.
So, I wonder, what's going on?
Is this something someone else has come across?
Okay, I've found the problem was the Python MySQL driver. Without using the .iterator() method a for loop would get stuck on the last element in the QuerySet. I have posted a more detailed answer on an expanded question here.
I was not using the Django recommended mysqlclient. I was using the
one created by Oracle/MySQL. There seems to be a bug that causes an
iterator to get "stuck" on the last element of the QuerySet in a for
loop and be trapped in an endless loop in certain circumstances.
Coming to think of it, it may well be that this is a design feature of the MySQL driver. I remember having a similar issue with a Java version of this driver before. Maybe I should just ditch MySQL and move to PostgreSQL?
I will try to raise a bug with Oracle anyways.

Django/Python: Best Practice/Advice on handling external IDs for Multiple Multi-directional External APIs

So this is more of a conceptual question, and I am really looking for someone to just help point me in the right direction. I am building a middleware platform where I will be pull data in from inbound channels, manipulating it, and then pushing it out the other door to outbound channels. I will need to store the external id for each of these records, but the kicker is, records will be pulled from multiple sources, and then pushed to multiple sources. A single record in my system will need to be tied to any number of external ids.
a quick model to work with:
class record(models.Model):
#id
Name = models.CharField(max_length=255, help_text="")
Description = models.CharField(max_length=255, help_text="")
category_id = model.ForeignKey(category)
class category(models.Model):
#id
name = models.CharField(max_length=255, help_text="")
description = models.CharField(max_length=255, help_text="")
class channel(models.Model):
#id
name = models.CharField(max_length=255, help_text="")
inbound = models.BooleanField()
outbound = models.BooleanField()
Obviously, I cannot add a new field to every model every time I add a new integration, that would be soooo 90s. The obvious would be to create another model to simply store the channel and record id with the unique id, and maybe this is the answer.
class external_ref(models.Model):
model_name = models.CharfieldField()
internal_id = models.IntegerField()
external_id = models.IntegerField()
channel_id = models.IntegerField()
class Meta:
unique_together = ('model', 'internal_id',)
While my example holds simply 4 models, I will be integrating records from 10-20 different models, so something I could implement an a global level would be optimal. Other things I have considered:
Overwriting the base model class to create a new "parent" class that also holds an alpha-numberic representation of every record in the db as unique.
Creating an abstract model to do the same.
Possibly storing a json reference with channel : external_id that I could ping on every record to see if it has an external reference.
I'm really an open book on this, and the internet has become increasingly overwhelming to sift through. Any best practices or advice would be much appreciated. Thanks in advance.
I have this exact issue and yes there is not much information on the web in using Django this way. Heres what Im doing - haven't used it long enough to determine if its "the best" way.
I have a class IngestedModel which tracks the source of the incoming objects as well as their external ids. This is also where you would put a unique_together constraint (on external_id and source)
class RawObject(TimeStampedModel):
"""
A Table to keep track of all objects ingested into the database and where they came from
"""
data = models.JSONField()
source = models.ForeignKey(Source,on_delete=models.PROTECT)
class IngestedModel(models.Model):
external_id = models.CharField(max_length=50)
source = models.ForeignKey(Source,on_delete=models.CASCADE)# 1 or 0
raw_objects = models.ManyToManyField(RawObject,blank=True)
class Meta:
abstract = True
then every model that is created from ingested data inherits from this IngestedModel. That way you know its source and you can use each external object for more than 1 internal object and vise versa.
class Customer(IngesteModel):
class Order(IngestedModel):
...
etc.
Now this means there is no "IngestedModel" table but that every model has a field for source, external_id and a reference to a raw object (many to many). This feels more compositional rather than inherited - no child tables which seems better to me. I would also love to hear feedback on the "right" way to do this.

Giving a model knowledge of a many-to-many related model in django

EDIT: Given responses in comments and answer I tried suggestion and I get some errors when trying to query , also doing the related name query does not get the right results (as seen in comments)
BusinessLocations.objects.all()
Error: QuerySet object has no attribute 'objects' is the error.
In either case, I did a dump of all the tables and see this:
auth_business_permissions', u'auth_permission', u'auth_user', u'auth_user_businesss', u'auth_user_user_permissions', u'django_admin_log',
u'django_content_type', u'django_migrations', u'django_session', u'ipaswdb_address', u'ipaswdb_billing', u'ipaswdb_billing_businesss',
u'ipaswdb_designation', u'ipaswdb_business', u'ipaswdb_business_business_locations', u'ipaswdb_businessinsurances', u'ipaswdb_businessinvoices',
'ipaswdb_businesslocations', u'ipaswdb_businessterm', u'ipaswdb_insurance', u'ipaswdb_insurance_businesss', u'ipaswdb_invoice', u'ipaswdb_employee',
u'ipaswdb_employeeinvoice', u'ipaswdb_employeelocations', u'ipaswdb_employeeterms', u'ipaswdb_specialty']
I have a ipaswdb_business_business_locations and a ipaswdb_businesslocations which seems strange to me, and I wonder if my database is just gunked up?
Original Question:
I have two models a Business and an Employee. I want them both to be aware of each other but not directly but through another model called a 'BusinessesLocation`. I can sort of express this in my models but it doesn't look or feel right. It is like only the employee knows of the businesses, and not vice vice versa.
I had another question opened to try to answer this but the answer was not 100% correct in that it didn't offer for a many to many it was more like a one to many. In this case: An employee can work at many locations (potentially being an employee of many businesses) and a business can have many locations having many employees.
Currently my models work where this shell script works:
someEmployee.business_locations.all()[0].business.business_name
and it works fine, I can get all the locations of a business an employee works at and via that infer the many businesses an employee might work for given the businesses locations.
But I cannot figure out how to go the other way, and find out all the employees a business has working for them and at which locations
My current (wrongish) models are like this:
class Employee(models.Model):
first_name = models.CharField(max_length = 50)
business_locations = models.ManyToManyField('BusinessLocations', through='EmployeeLocations')
class EmployeeLocations(models.Model):
employee = models.ForeignKey('Employee', on_delete=models.CASCADE)
business_location = models.ForeignKey('BusinessLocations', on_delete=models.CASCADE)
created_at=models.DateField(auto_now_add=True)
updated_at=models.DateField(auto_now=True)
def __str__(self):
return self.provider.first_name
class BusinessLocations(models.Model):
address = models.ForeignKey('Address', on_delete= models.SET_NULL, null=True)
business = models.ForeignKey('Business', on_delete=models.CASCADE)
doing_business_as = models.CharField(max_length = 255)
created_at=models.DateField(auto_now_add=True)
updated_at=models.DateField(auto_now=True)
def __str__(self):
return self.doing_business_as
class Business(models.Model):
business_name = models.CharField(max_length=50)
business_locations = I need something here no idea how
Bellow is some pseudo shell code demonstrating how I would like my models to work:
#create a new business location assume business has been created
newLocation = Address(...)
business.business_locations.add(newLocation, doing_business_as='alternative name maybe')
#assume employee exists
#add a new business location to the employee
#when i say selected business the form would have current employee then in its locations
#you'd have to select a business first, and get a list of all that businesses locations and you
#you could add the business location and then select another business with all ITS locations
# and add one there too if you wish
employee.employee_locations.add(selectedBusiness.business_locations[0])
employee.employee_locations.add(anotherSelectedBusiness.business_locations[1])
Below is what I cannot figure out how to do, vice versa...
#now lets see which businesses the employee works for.
for business in employee.business_locations
business.business_name
#and lets see each businesses employees:
for employee in Employee.objects.all()
employee.
?? No idea how to build the models to represent these relationships
I can get an employees business locations just fine, but I cannot get the above examples of getting a list of employees for a business. Not sure what I need to adjust (or methods I might need?) to get this to work like I want in my shell example.
What you're missing is Django's concept of related objects.
When you define a relationship in a model (i.e., a ForeignKey, OneToOneField, or ManyToManyField), instances of that model will have a convenient API to access the related objects.
You can access the related objects both in queries and as a manager attribute on your models. See the examples in the documentation. In your case this would look something like:
# Now lets see which businesses the employee works for:
Business.objects.filter(businesslocations__employee=employee).distinct()
# And let's see each business's employees:
Employee.objects.filter(business_locations__business=business).distinct()

Categories

Resources