Handling Django Model Fields that may be "None" to avoid AttributeError - python

I have a Django model that looks something like this :
class Candidate(models.Model):
first_name = models.CharField(max_length=30, blank=True, null=True)
middle_name = models.CharField(max_length=30, blank=True, null=True)
last_name = models.CharField(max_length=30, blank=True, null=True)
current_job = models.ForeignKey(
Job,
on_delete=models.SET_NULL,
blank=True,
null=True,
default=None,
)
I get an instance of "Candidate", and try to save some of the values into a dictionary
candidate = Candidate.objects.get(first_name = "John")
data['first_name'] = candidate.first_name
data['last_name'] = candidate.last_name
data['company_name'] = candidate.current_job.company
This works fine when all values and foreign-keys are properly populated.
However, when any of the values of the fields are None, especially important regarding the ForeignKey relationships, I'll hit an AttributeError, something like : 'NoneType' object has no attribute 'company'
I want to properly handle the "None" case for any Field in the Model.
I've found these two work-arounds for it right now, but neither seem satisfactory to me.
A) I can put a try-except around EACH and EVERY field (which doesn't seem correct as my models get to ~20 fields)
try:
data['first_name'] = candidate.first_name
data['last_name'] = candidate.last_name
except:
pass
try:
data['company_name'] = candidate.current_job.company
B) I can convert the instance to a dict like this and use a .get() since that never raises an Exception.
candidate_dict = candidate.__dict__
data['first_name'] = candidate_dict.get('first_name')
Is there a better way of handling the possibility of field values being None without handling an AttributeError exception on each and every single field?

First You need to check following query will return a record or not. You can put in try catch and handle the DoesNotExist exception.
candidate = Candidate.objects.get(first_name="John")
if candidate record present then you do not need to handle exception for
first_name, middle_name, last_name fields.
Since current_job is FK field and it can be null so before getting Job model fields data you first need to check the candidate related current_job field is not null

Related

Get all records with the same Field1 and different Field2 Django ORM

Good afternoon!
I have a model of the following format
class Order(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='orders')
address = models.CharField(max_length=255, null=True, blank=True)
....
I need to get all the records with the same address, but with different user.
Tell me, please, how can I fulfill this request?
A request in this format outputs an error
orders = Order.objects\
.filter(...)\
.distinct('address', 'user')\
.annotate(Count('address')\
.filter(address__count_gt=1)
NotImplementedError: annotate() + distinct(fields) is not implemented.
And if so, then count counts incorrectly and I lose data because of values (I need not to lose the order object)
orders = Order.objects\
.filter(...)\
.values('address', 'user')\
.distinct()\
.annotate(Count('address')\
.filter(address__count_gt=1)

Django ProgrammingError must appear in the GROUP BY clause or be used in an aggregate function using annotate func

Here is my model.
class EnvAi(models.Model):
dateandtime = models.CharField(db_column='DateAndTime', max_length=50, blank=True, null=True) # Field name made lowercase.
tagname = models.CharField(db_column='TagName', max_length=50, blank=True, null=True) # Field name made lowercase.
datavalue = models.CharField(db_column='DataValue', max_length=50, blank=True, null=True) # Field name made lowercase.
dateandtime sample is "2022-08-10 10:10:20"
dateandtime field is charField and i have to slice date and time to get what I want.
# views.py
queryset = EnvAi.objects.values('datavalue', 'tagname').annotate(slicedate=Substr('dateandtime', 1, 10)) # slicedate => "2022-08-10"
# Error Code
queryset = queryset.values('tagname', 'slicedate').annotate(max_value=Max('datavalue'))
after create 'slicedate' variable, I want to get Max value each tagname and slicedate.
but i get error
ProgrammingError: column "Env_AI.DateAndTime" must appear in the GROUP BY clause or be used in an aggregate function
I tried using raw sql in the database using query, but there was no problem.
How can i solve this problem?

Django: how to use .filter( ) method in django?

I am trying to display quiz only for users that are registered in a particular course, i.e if a user is registered in a Frontend Crash Course i want them to see only the quiz related to that course they are registered in, and not all the quiz from the db.
i have a model UserCourse where i am storing all the courses a user have enrolled in, when i try filtering by that models while user_course is get like this below
user_course = UserCourse.objects.get(user=request.user)
quizzes = Quiz.objects.filter(course__usercourse=user_course).annotate(questions_count=Count('questions'))
i get this error get() returned more than one UserCourse -- it returned 3! Now i have changed .get() to .filter() like this
user_course = UserCourse.objects.filter(user=request.user)
quizzes = Quiz.objects.filter(course__usercourse=user_course).annotate(questions_count=Count('questions'))
i then get this error The QuerySet value for an exact lookup must be limited to one result using slicing.
What is the right way to write this query.
models.py
class UserCourse(models.Model):
user = models.ForeignKey(User , null = False , on_delete=models.CASCADE)
course = models.ForeignKey(Course , null = False , on_delete=models.CASCADE, related_name="usercourse")
class Quiz(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="quizzes")
title = models.CharField(max_length=255)
course = models.ForeignKey(Course, on_delete=models.SET_NULL, null=True, related_name="quizzes")
date = models.DateTimeField(auto_now_add=True)
slug = models.SlugField(unique=True)
user_course = models.ForeignKey(UserCourse, on_delete=models.SET_NULL, null=True)
def __str__(self):
return self.title
The Problem in the Second Line
user_course = UserCourse.objects.filter(user=request.user)
quizzes=Quiz.objects.filter(course__usercourse=user_course).annotate(questions_count=Count('questions'))
remember that when You are using filter you get QuerySet not one object
if you want to return the quizes those related to user_course_queryset you can use __in filter
print(user_course) # print it to understand more
quizzes=Quiz.objects.filter(course__usercourse__in=user_course)
this will Return every Quiz Related to the QuerySet objects

Django: Cross referencing Model objects within a model - best practise?

I'm currently going around in a bit of a "QuerySet' object has no attribute '' <> app.models.DoesNotExist: Messages matching query does not exist loop.
Essentially, I'm trying to define "last_activity" on a Room model that is referencing the time at which the last Message associated to that room was sent. This is my attempt:
class Room(models.Model):
title = models.CharField(max_length=255)
staff = models.BooleanField(default=False)
slug = models.SlugField(max_length=250, default='')
banner = models.ImageField(storage=USER_UPLOAD_LOC, null=True, blank=True)
def last_activity(self):
last_persisted_message = Messages.objects.filter(where=self.title).order_by('-sent_at')[:1]
return last_persisted_message.sent_at
class Messages(models.Model):
room = models.ForeignKey(Room, on_delete=models.CASCADE)
where = models.CharField(max_length=255)
message = models.TextField(default='')
user = models.ForeignKey(settings.AUTH_USER_MODEL)
username_from = models.CharField(max_length=255)
username_to = models.CharField(max_length=255, default='all')
sent_at = models.DateTimeField(default=datetime.datetime.now)
I've tried so many things now and referenced the query set documentation and nothing seems to be working.
I can also confirm that the "where" field for the Messages model is populated when a message is created by {{ room.title }}. I'm using a web socket connection on the client side to pass a "message" back to the websocket consumer.py which then persists the message to the DB).
If you slice the queryset with [:1] you will obtain a queryset containing at most one item, but not an item itself. You can use [0] to obtain the first item, or, .first():
def last_activity(self):
last_persisted_message = Messages.objects \
.filter(where=self.slug) \
.order_by('-sent_at') \
.first()
if last_persisted_message is not None:
return last_persisted_message.sent_at
If you use [0] and there is no such item, then the program will raise an IndexError (since there is no item with index 0).
In case there is no such Messages object (that satisfies the filter(..)), then last_persisted_message will be None, so you have to find a way to resolve that case. Here we return None in that case.

How to create django database model that "knows" what kind of category it is?

In Django, I have the following models.py
class Product(RandomPrimaryIdModel):
title = models.CharField(max_length=20, blank=True, null=True)
price = models.CharField(max_length=20, blank=True, null=True)
condition = models.CharField(max_length=20, blank=True, null=True)
class Mattress(Product):
length = models.CharField(max_length=50)
size = models.CharField(max_length=5)
class Pillow(Product):
shape= models.CharField(max_length=50)
comfort= models.CharField(max_length=5)
The idea is that there's a "product" model and several "product_type" models. I'm trying to create a database scheme that relates the two. The end goal is so that when I given access to a primary id for an object whose product_type is unknown, I can simply query/filter that object to find out what the product_type is of the object.
I know that sounds a bit confusing, but how would I go about implementing the correct way? The current scheme (the one above) is not the correct solution I believe.
According to the docs on multi-table inheritance you can reference the lowercase name of the model. In your case to find out the "product type" you'd do something like:
product = Product.objects.get(id=12)
try:
mattress = product.mattress
is_mattress = True
except Mattress.DoesNotExist:
is_mattress = False
You could abstract this out to a helper method that would do the tests for you and return the type as a string or enum of some sort.
If you have a reference to an object, can't you use something like:
p = Product.objects.get(id=1)
class_of_p = str(p.__class__)
and then parse the resulting string
"<class 'whatever.models.Pillow'>"
to find what you need? Apologies if I'm missing something.

Categories