Django Models with Choices as Integer - django

I am trying to make a feedback app in Django, but I can not make my evaluations.
In my models.py, I have 5 choices from very bad to excellent. But I want them to be usable as numbers so I can evaluate the overall value.
After reading Set Django IntegerField by choices=... name I changed my Ratings from VERYBAD = 'Very bad' to VERYBAD = 1 but no I can't even save my value/form.
Feedback(models.Model):
event = models.ForeignKey(Event)
# Choice
VERYBAD = 'Very bad' #old
BAD = 2 #new
OKAY = 3
GOOD = 4
EXCELLENT = 5
RATING = (
(VERYBAD, 'Very bad'),
(BAD, 'Bad'),
(OKAY, 'Okay'),
(GOOD, 'Good'),
(EXCELLENT, 'Excellent'),
)
# The ratings
organisation = models.CharField(
max_length=9,
choices=RATING,
default=OKAY,
)
....
So in my view, I thought I can do the math but I can not.
def rating(request, event_id):
myevent = Event.objects.get(id=event_id)
feedback_items = Feedback.objects.filter(event=myevent)
num_of_items = len(feedback_items)
def evaluate(feedback_items):
# The overall rating
organisation = 0
for item in feedback_items:
organisation += item.organisation
organisation /= num_of_items
context = {'feedback_items':feedback_items,
'num_of_items': num_of_items,
'myevent': myevent,}
return render(request, 'feedback/rating.html', context)
`

You have to make organisation an IntegerField, obviously - else you can't hope to do integer operations. If you already have this in production, use a set of migrations to add a new IntegerField, populate it from the existing CharField values (using a dict for mapping old string values to new int values), and finally getting rid of the original field.
Also, you may want to learn how to properly use Django's ORM features like getting related models instances and performing aggregations at the DB level, ie:
from django.db.models import Sum, Count, Avg
def rating(request, event_id):
myevent = Event.objects.get(id=event_id)
# this is how you get related models...
feedback_items = myevent.feedback_set.all()
# and this is how you use SQL aggregation functions:
values = feedback_items.aggregate(
sum=Sum('organisation'),
count=Count('pk'),
avg=Avg('organisation')
)
# Now 'values' should be a dict with 'sum', 'count' and 'avg'
# keys, and values['avg'] should be equal to
# float(values['sum']) / values['count']
# FWIW you probably don't need the 'sum' field at all
# I mentionned it so you can check whether 'avg' value
# is correct...
context = {
'feedback_items':feedback_items,
'num_of_items': values['count'],
'avg': values['avg'],
'myevent': myevent,
}
# etc

Related

Django Annotation Count with Subquery & OuterRef

I'm trying to create a high score statistic table/list for a quiz, where the table/list is supposed to be showing the percentage of (or total) correct guesses on a person which was to be guessed on. To elaborate further, these are the models which are used.
The Quiz model:
class Quiz(models.Model):
participants = models.ManyToManyField(
User,
through="Participant",
through_fields=("quiz", "correct_user"),
blank=True,
related_name="related_quiz",
)
fake_users = models.ManyToManyField(User, related_name="quiz_fakes")
user_quizzed = models.ForeignKey(
User, related_name="user_taking_quiz", on_delete=models.CASCADE, null=True
)
time_started = models.DateTimeField(default=timezone.now)
time_end = models.DateTimeField(blank=True, null=True)
final_score = models.IntegerField(blank=True, default=0)
This model does also have some properties; I deem them to be unrelated to the problem at hand.
The Participant model:
class Participant(models.Model): # QuizAnswer FK -> QUIZ
guessed_user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="clicked_in_quiz", null=True
)
correct_user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="solution_in_quiz", null=True
)
quiz = models.ForeignKey(
Quiz, on_delete=models.CASCADE, related_name="participants_in_quiz"
)
#property
def correct(self):
return self.guessed_user == self.correct_user
To iterate through what I am trying to do, I'll try to explain how I'm thinking this should work:
For a User in User.objects.all(), find all participant objects where the user.id equals correct_user(from participant model)
For each participantobject, evaluate if correct_user==guessed_user
Sum each participant object where the above comparison is True for the User, represented by a field sum_of_correct_guesses
Return a queryset including all users with parameters [User, sum_of_correct_guesses]
^Now ideally this should be percentage_of_correct_guesses, but that is an afterthought which should be easy enough to change by doing sum_of_correct_guesses / sum n times of that person being a guess.
Now I've even made some pseudocode for a single person to illustrate to myself roughly how it should work using python arithmetics
# PYTHON PSEUDO QUERY ---------------------
person = get_object_or_404(User, pk=3) # Example-person
y = Participant.objects.filter(
correct_user=person
) # Find participant-objects where person is used as guess
y_corr = [] # empty list to act as "queryset" in for-loop
for el in y: # for each participant object
if el.correct: # if correct_user == guessed_user
y_corr.append(el) # add to queryset
y_percentage_corr = len(y_corr) / len(y) # do arithmetic division
print("Percentage correct: ", y_percentage_corr) # debug-display
# ---------------------------------------------
What I've tried (with no success so far), is to use an ExtensionWrapper with Count() and Q object:
percentage_correct_guesses = ExpressionWrapper(
Count("pk", filter=Q(clicked_in_quiz=F("id")), distinct=True)
/ Count("solution_in_quiz"),
output_field=fields.DecimalField())
all_users = (
User.objects.all().annotate(score=percentage_correct_guesses).order_by("score"))
Any help or directions to resources on how to do this is greatly appreciated :))
I found an answer while looking around for related problems:
Django 1.11 Annotating a Subquery Aggregate
What I've done is:
Create a filter with an OuterRef() which points to a User and checks if Useris the same as correct_person and also a comparison between guessed_person and correct_person, outputs a value correct_user in a queryset for all elements which the filter accepts.
Do an annotated count for how many occurrences there are of a correct_user in the filtered queryset.
Annotate User based on the annotated-count, this is the annotation that really drives the whole operation. Notice how OuterRef() and Subquery are used to tell the filter which user is supposed to be correct_user.
Below is the code snippet which I made it work with, it looks very similar to the answer-post in the above linked question:
from django.db.models import Count, OuterRef, Subquery, F, Q
crit1 = Q(correct_user=OuterRef('pk'))
crit2 = Q(correct_user=F('guessed_user'))
compare_participants = Participant.objects.filter(crit1 & crit2).order_by().values('correct_user')
count_occurrences = compare_participants.annotate(c=Count('*')).values('c')
most_correctly_guessed_on = (
User.objects.annotate(correct_clicks=Subquery(count_occurrences))
.values('first_name', 'correct_clicks')
.order_by('-correct_clicks')
)
return most_correctly_guessed_on
This works wonderfully, thanks to Oli.

django annotate with queryset

I have Users who take Surveys periodically. The system has multiple surveys which it issues at set intervals from the submitted date of the last issued survey of that particular type.
class Survey(Model):
name = CharField()
description = TextField()
interval = DurationField()
users = ManyToManyField(User, related_name='registered_surveys')
...
class SurveyRun(Model):
''' A users answers for 1 taken survey '''
user = ForeignKey(User, related_name='runs')
survey = ForeignKey(Survey, related_name='runs')
created = models.DateTimeField(auto_now_add=True)
submitted = models.DateTimeField(null=True, blank=True)
# answers = ReverseForeignKey...
So with the models above a user should be alerted to take survey A next on this date:
A.interval + SurveyRun.objects.filter(
user=user,
survey=A
).latest('submitted').submitted
I want to run a daily periodic task which queries all users and creates new runs for all users who have a survey due according to this criteria:
For each survey the user is registered:
if no runs exist for that user-survey combo then create the first run for that user-survey combination and alert the user
if there are runs for that survey and none are open (an open run has been created but not submitted so submitted=None) and the latest one's submitted date plus the survey's interval is <= today, create a new run for that user-survey combo and alert the user
Ideally I could create a manager method which would annotate with a surveys_due field like:
users_with_surveys_due = User.objects.with_surveys_due().filter(surveys_due__isnull=False)
Where the annotated field would be a queryset of Survey objects for which the user needs to submit a new round of answers.
And I could issue alerts like this:
for user in users_with_surveys_due.all():
for survey in user.surveys_due:
new_run = SurveyRun.objects.create(
user=user,
survey=survey
)
alert_user(user, run)
However I would settle for a boolean flag annotation on the User object indicating one of the registered_surveys needs to create a new run.
How would I go about implementing something like this with_surveys_due() manager method so Postgres does all the heavy lifting? Is it possible to annotate with a collection objects, like a reverse FK?
UPDATE:
For clarity here is my current task in python:
def make_new_runs_and_alert_users():
runs = []
Srun = apps.get_model('surveys', 'SurveyRun')
for user in get_user_model().objects.prefetch_related('registered_surveys', 'runs').all():
for srvy in user.registered_surveys.all():
runs_for_srvy = user.runs.filter(survey=srvy)
# no runs exist for this registered survey, create first run
if not runs_for_srvy.exists():
runs.append(Srun(user=user, survey=srvy))
...
# check this survey has no open runs
elif not runs_for_srvy.filter(submitted=None).exists():
latest = runs_for_srvy.latest('submitted')
if (latest.submitted + qnr.interval) <= timezone.now():
runs.append(Srun(user=user, survey=srvy))
Srun.objects.bulk_create(runs)
UPDATE #2:
In attempting to use Dirk's solution I have this simple example:
In [1]: test_user.runs.values_list('survey__name', 'submitted')
Out[1]: <SurveyRunQuerySet [('Test', None)]>
In [2]: test_user.registered_surveys.values_list('name', flat=True)
Out[2]: <SurveyQuerySet ['Test']>
The user has one open run (submitted=None) for the Test survey and is registered to one survey (Test). He/She should not be flagged for a new run seeing as there is an un-submitted run outstanding for the only survey he/she is registered for. So I create a function encapsulating the Dirk's solution called get_users_with_runs_due:
In [10]: get_users_with_runs_due()
Out[10]: <UserQuerySet [<User: test#gmail.com>]> . # <-- should be an empty queryset
In [107]: for user in _:
print(user.email, i.has_survey_due)
test#gmail.com True # <-- should be false
UPDATE #3:
In my previous update I had made some changes to the logic to properly match what I wanted but neglected to mention or show the changes. Here is the query function below with comments by the changes:
def get_users_with_runs_due():
today = timezone.now()
survey_runs = SurveyRun.objects.filter(
survey=OuterRef('pk'),
user=OuterRef(OuterRef('pk'))
).order_by('-submitted')
pending_survey_runs = survey_runs.filter(submitted__isnull=True)
surveys = Survey.objects.filter(
users=OuterRef('pk')
).annotate(
latest_submission_date=Subquery(
survey_runs.filter(submitted__isnull=False).values('submitted')[:1]
)
).annotate(
has_survey_runs=Exists(survey_runs)
).annotate(
has_pending_runs=Exists(pending_survey_runs)
).filter(
Q(has_survey_runs=False) | # either has no runs for this survey or
( # has no pending runs and submission date meets criteria
Q(has_pending_runs=False, latest_submission_date__lte=today - F('interval'))
)
)
return User.objects.annotate(has_survey_due=Exists(surveys)).filter(has_survey_due=True)
UPDATE #4:
I tried to isolate the issue by creating a function which would make most of the annotations on the Surveys by user in an attempt to check the annotation on that level prior to querying the User model with it.
def annotate_surveys_for_user(user):
today = timezone.now()
survey_runs = SurveyRun.objects.filter(
survey=OuterRef('pk'),
user=user
).order_by('-submitted')
pending_survey_runs = survey_runs.filter(submitted=None)
return Survey.objects.filter(
users=user
).annotate(
latest_submission_date=Subquery(
survey_runs.filter(submitted__isnull=False).values('submitted')[:1]
)
).annotate(
has_survey_runs=Exists(survey_runs)
).annotate(
has_pending_runs=Exists(pending_survey_runs)
)
This worked as expected. Where the annotations were accurate and filtering with:
result.filter(
Q(has_survey_runs=False) |
(
Q(has_pending_runs=False) &
Q(latest_submission_date__lte=today - F('interval'))
)
)
produced the desired results: An empty queryset where the user should not have any runs due and vice-versa. Why is this not working when making it the subquery and querying from the User model?
To annotate users with whether or not they have a survey due, I'd suggest to use a Subquery expression:
from django.db.models import Q, F, OuterRef, Subquery, Exists
from django.utils import timezone
today = timezone.now()
survey_runs = SurveyRun.objects.filter(survey=OuterRef('pk'), user=OuterRef(OuterRef('pk'))).order_by('-submitted')
pending_survey_runs = survey_runs.filter(submitted__isnull=True)
surveys = Survey.objects.filter(users=OuterRef('pk'))
.annotate(latest_submission_date=Subquery(survey_runs.filter(submitted__isnull=False).values('submitted')[:1]))
.annotate(has_survey_runs=Exists(survey_runs))
.annotate(has_pending_runs=Exists(pending_survey_runs))
.filter(Q(has_survey_runs=False) | Q(latest_submission_date__lte=today - F('interval')) & Q(has_pending_runs=False))
User.objects.annotate(has_survey_due=Exists(surveys))
.filter(has_survey_due=True)
I'm still trying to figure out how to do the other one. You cannot annotate a queryset with another queryset, values must be field equivalents. Also you cannot use a Subquery as queryset parameter to Prefetch, unfortunately. But since you're using PostgreSQL you could use ArrayField to list the ids of the surveys in a wrapped value, but I haven't found a way to do that, as you can't use aggregate inside a Subquery.

Show fields based on the value of another field in flask admin

I have database tables with a 'TYPE' column and many other fields. In many cases, certain column values are null based on the value of 'TYPE'.
E.g.
if I have a product table , with TYPE having either 'car' or 'helicopter'. The columns are:
vertical speed, horizontal speed, and horn amplitude.
In the case of 'car' types, vertical speed should always be null , and in the case of 'helicopter' , horn amplitude should always be null.
In flask admin, is there any way to hide the fields from being submitted based on the currently selected TYPE's value?
It is fine if it is a UI level change (i.e. no backend validation is required for security/consistency purposes).
In my real life scenario, there are over 10 columns with 5+ being null in cases, so it would be very helpful if those fields can be removed in the UI (since it makes the form very long and prone to errors).
I am using flask sqlalchemy as the backend for my flask admin.
Fun question. Here is a working solution. So basically you have product types, and each type has certain valid attributes (e.g. car and honking loudness). You can also have general attributes irregardless of the type, e.g. the name of each product.
On the form_prefill, you check what fields are valid for the product type. Then you throw away the invalid fields from the form, and return the form again. It's actually pretty straightforward.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
import flask_admin as admin
from flask_admin.contrib import sqla
app = Flask(__name__)
app.secret_key = 'arstt'
db = SQLAlchemy(app)
class Product(db.Model):
id = db.Column(db.Integer, primary_key=True)
type = db.Column(db.String)
name = db.Column(db.String)
vertical_speed = db.Column(db.Integer)
rotor_rpm = db.Column(db.Integer)
honking_loudness = db.Column(db.Integer)
def __str__(self):
return "{}".format(self.name)
class ProductView(sqla.ModelView):
general_product_attributes = ['name']
product_attributes_per_product_type_dict = {
'driving': ['honking_loudness'],
'flying': ['rotor_rpm', 'vertical_speed']
}
def on_form_prefill(self, form, id):
product = self.get_one(id)
form_attributes = self.general_product_attributes + self.product_attributes_per_product_type_dict[product.type]
for field in list(form):
if field.name not in form_attributes:
delattr(form, field.name)
return form
db.create_all()
admin = admin.Admin(app, name='Example: SQLAlchemy', template_mode='bootstrap3')
admin.add_view(ProductView(Product, db.session))
helicopter = Product(type='flying', name='helicopter1', vertical_speed=99)
car = Product(type='driving', name='car2', honking_loudness=33)
db.session.add(helicopter)
db.session.add(car)
db.session.commit()
Note that this only works for the edit form, all attribtutes are still being displayed on the create form because it is not certain yet what type an product will be.
You can override create_form for create form and on_prefill_form for edit form.
in this functions you can pass some parameters to fields using form_widget_args
def create_form(self):
form = super(JobsView, self).create_form()
# kw = decide which fields to show or hide
# kw["vertical_speed"]["class"] = "hide"
self.form_widget_args = kw
return form
def on_form_prefill(self, form, id):
# kw = decide which fields to show or hide
# kw["vertical_speed"]["class"] = "hide"
self.form_widget_args = kw

Filtering queryset if one value is greater than another value

I am trying to filter in view my queryset based on relation between 2 fields .
however always getting the error that my field is not defined .
My Model has several calculated columns and I want to get only the records where values of field A are greater than field B.
So this is my model
class Material(models.Model):
version = IntegerVersionField( )
code = models.CharField(max_length=30)
name = models.CharField(max_length=30)
min_quantity = models.DecimalField(max_digits=19, decimal_places=10)
max_quantity = models.DecimalField(max_digits=19, decimal_places=10)
is_active = models.BooleanField(default=True)
def _get_totalinventory(self):
from inventory.models import Inventory
return Inventory.objects.filter(warehouse_Bin__material_UOM__UOM__material=self.id, is_active = true ).aggregate(Sum('quantity'))
totalinventory = property(_get_totalinventory)
def _get_totalpo(self):
from purchase.models import POmaterial
return POmaterial.objects.filter(material=self.id, is_active = true).aggregate(Sum('quantity'))
totalpo = property(_get_totalpo)
def _get_totalso(self):
from sales.models import SOproduct
return SOproduct.objects.filter(product__material=self.id , is_active=true ).aggregate(Sum('quantity'))
totalso = property(_get_totalpo)
#property
def _get_total(self):
return (totalinventory + totalpo - totalso)
total = property(_get_total)
And this is line in my view where I try to get the conditional queryset
po_list = MaterialFilter(request.GET, queryset=Material.objects.filter( total__lte = min_quantity ))
But I am getting the error that min_quantity is not defined
What could be the problem ?
EDIT:
My problem got solved thank you #Moses Koledoye but in the same code I have different issue now
Cannot resolve keyword 'total' into field.Choices are: am, author, author_id, bom, bomversion, code, creation_time, description, id, inventory, is_active, is_production, itemgroup, itemgroup_id, keywords, materialuom, max_quantity, min_quantity, name, pomaterial, produce, product, slug, trigger_quantity, uom, updated_by, updated_by_id, valid_from, valid_to, version, warehousebin
Basically it doesn't show any of my calculated fields I have in my model.
Django cannot write a query which is conditioned on a field whose value is unknown. You need to use a F expression for this:
from django.db.models import F
queryset = Material.objects.filter(total__lte = F('min_quantity'))
And your FilterSet becomes:
po_list = MaterialFilter(request.GET, queryset = Material.objects.filter(total__lte=F('min_quantity')))
From the docs:
An F() object represents the value of a model field or annotated
column. It makes it possible to refer to model field values and
perform database operations using them without actually having to pull
them out of the database into Python memory

Evaluating the same object through span relation in qs.excude()

Consider a Hotel with Room objects and Reservation objects for those rooms. I want to find which rooms are available at a given period or (particularly in the example below) from which date onward.
A reservation can be 'deleted', which is obtained by settings the "Live" field. So they're not actually deleted but just inactive, and this would need to remain this way.
>>> indate = "20141225"
>>> Room.objects.exclude(
(models.Q(reservation__live=True , reservation__date_out__gt=indate)
| models.Q(reservation__date_out__isnull=True, reservation__live=True))
)
Problem statement: The above code has an unfortunate side effect: when there is a reservation with live=True that is outside the period, and when there's another reservation live=False within the period for the same room, then that Room will be excluded. This should not be the case: since the reservation within the period I'm asking for is not set to live=True, it should be not be taken into account.
It looks like my query above is not considering the same reservation when doing the (live=True and date_out__gt=indate) comparison through the room-reservation relation.
Question: is there a way, within the exclude(), to ensure the same reservation is considered in the comparison?
I tried playing around with negative models.Q (~Models.Q) to no avail.
Note the above is just a code extract (where the issue resides) out of a much larger query. Therefore I cannot simply do qs.filter(reservation__live=True). Moving over the query to filter instead of exclude doesn't seem to be an option.
Edit: adding my simplified models on request, below.
Edit2: adding some test data explaining the issue, and adding a chained exclude() as per #knbk's suggestion.
class AvailableRoomManager(models.Manager):
def available_with_Q(self, indate, outdate):
qs = super(AvailableRoomManager, self).get_queryset()
if indate and outdate:
qs = qs.exclude(models.Q(reservation__date_out__gt=indate, reservation__date_in__lt=outdate, reservation__live=True)
| models.Q(reservation__date_out__isnull=True, reservation__date_in__isnull=False, reservation__date_in__lt=outdate, reservation__live=True))
elif indate and not outdate:
qs = qs.exclude((models.Q(reservation__date_out__gt=indate, reservation__date_in__isnull=False, reservation__live=True)
| models.Q(reservation__date_out__isnull=True, reservation__date_in__isnull=False, reservation__live=True)))
return qs
def available_with_chained_excludes(self, indate, outdate):
qs = super(AvailableRoomManager, self).get_queryset()
if indate and outdate:
qs = qs.exclude(reservation__date_out__gt=indate, reservation__date_in__lt=outdate, reservation__live=True) \
.exclude(reservation__date_out__isnull=True, reservation__date_in__isnull=False, reservation__date_in__lt=outdate, reservation__live=True)
elif indate and not outdate:
qs = qs.exclude(reservation__date_out__gt=indate, reservation__date_in__isnull=False, reservation__live=True) \
.exclude(reservation__date_out__isnull=True, reservation__date_in__isnull=False, reservation__live=True)
return qs
class Room(models.Model):
name = models.CharField(max_length=30, unique=True)
objects = models.Manager()
available_rooms = AvailableRoomManager()
def __str__(self):
return self.name
class Reservation(models.Model):
date_in = models.DateField()
date_out = models.DateField(blank=True, null=True)
room = models.ForeignKey(Room)
live = LiveField() # See django-livefield; to do deletion. Basically adds a field "live" to the model.
objects = LiveManager()
all_objects = LiveManager(include_soft_deleted=True)
The problem pops up in the exclude() statements above when there is an active (live=True) outside the period searching for, and when there is an inactive (live != True) inside the period I'm searching for.
Some simple test data using the above models, showing what the issue is about:
# Let's make two rooms, R001 and R002
>>> room1 = Room.objects.get_or_create(name="R001")[0]
>>> room2 = Room.objects.get_or_create(name="R002")[0]
# First reservation, with no date_out, is created but then "deleted" by setting field 'live' to False
>>> res1 = Reservation.objects.get_or_create(date_in="2014-12-01", date_out=None, room=room1)[0]
>>> res1.live = False
>>> res1.save()
# Second reservation in same room is created with date_out set to Dec 15th
>>> res2 = Reservation.objects.get_or_create(date_in="2014-12-01", date_out="2014-12-15", room=room1)[0]
# Here I'd expect to have R001 listed as well... this is not the case
>>> Room.available_rooms.available_with_Q("2014-12-16", "")
[<Room: R002>]
>>> Room.available_rooms.available_with_chained_excludes("2014-12-16", "")
[<Room: R002>]
# As a test, when changing "deleted" res1's Room to room2, the manager does return R001
>>> res1.room = room2
>>> res1.save()
>>> Room.available_rooms.available_with_Q("2014-12-16", "")
[<Room: R001>, <Room: R002>]
>>> Room.available_rooms.available_with_chained_excludes("2014-12-16", "")
[<Room: R001>, <Room: R002>]
I tested it with your github project, and I got the same results as you did. It seems that, while filter correctly converts multiple related-object filters to an INNER JOIN, exclude converts it in some sort of subquery where each filter (even within the same call) is checked on its own.
I've found a workaround for this, and that is to explicitly create the subquery of reservations:
elif indate and not outdate:
ress = Reservation.objects.filter(Q(live=True, date_in__isnull=False), Q(date_out__gt=indate) | Q(date_out__isnull=True))
rooms = Room.objects.exclude(reservation__in=ress)
etc...
Btw, if you ever need to use filter instead of exclude, the following queries are always identical, and in fact, this is what Django does internally:
Room.objects.exclude(<some_filter>)
Room.objects.filter(~Q(<some_filter>))
You want to not allow a checkin if there is already a reservation in that time, or a user would like to checkout when there is already a reservation placed:
first case : R_IN User_IN R_out
second case: R_IN User_IN (the room is considered busy, so no more check ins)
third case : User_IN R_IN User_OUT
Room.objects.exclude(
models.Q(reservation__live=False, reservation__date_in__lte=indate, reservation__date_out__gte=indate)
|
models.Q(reservation__live=False, reservation__date_in__lte=indate, reservation__date_out__isnull=True)
|
models.Q(reservation__live=False, reservation__date_in__gte=indate, reservation__date_in__lte=outdate)
)
if you need to take into account the case where outdate is not available you could simply factor out the last case, handling how it's best for you, maybe removing that room altogether because of possible conflicts or forcing the user to leave a day before the reservation you already have checks in