In my models.py I filter for two cases. I check if there is:
a ticket_on_sale
ticket_on_sale_soon
If 2) is given but 1) not, then the method should give back True. I filter on a variable level (after the database is already hit). I wonder if there is a more solid way. What I did doesn't feel right. Do you have suggestions?
models.py
class Event(TimeStampedModel):
organizer = models.ForeignKey(
Organizer,
on_delete=models.PROTECT,
related_name='events',
) # PROTECT = don't allow to delete the organizer if an event exists
name = models.CharField(
max_length=50,
verbose_name=_("Event Title"),
)
slug = models.SlugField(
validators=[SlugBlackList()],
verbose_name=_("Event Link"),
)
currency = models.CharField(
max_length=10,
choices=CURRENCY_CHOICES,
verbose_name=_("Currency"),
)
status = models.CharField(
max_length=8,
choices=EventStatus.CHOICES,
default=EventStatus.DRAFT,
verbose_name=_("Status"),
)
venue_address = models.TextField(
null=True,
blank=True,
verbose_name=_("Location address"),
)
start_date = models.DateTimeField(verbose_name=_("Start date"))
end_date = models.DateTimeField(verbose_name=_("End date"))
#cached_property
def only_scheduled_tickets(self):
tickets = self.tickets.all()
ticket_on_sale = list(filter(
lambda ticket: ticket.is_on_sale() and ticket.is_available(),
tickets,
))
ticket_on_sale_soon = list(filter(
lambda ticket: ticket.is_on_sale() and not ticket.is_available(),
tickets,
))
if ticket_on_sale_soon and not ticket_on_sale:
return True
class Ticket(TimeStampedModel):
event = models.ForeignKey(
Event,
on_delete=models.CASCADE,
related_name='tickets',
) # CASCADE = delete the ticket if the event is deleted
tax = models.ForeignKey(
'Tax',
on_delete=models.PROTECT,
related_name='tickets',
blank=True,
null=True,
) # PROTECT = don't allow to delete the ticket tax if a ticket exists
name = models.CharField(
max_length=100,
verbose_name=_("Ticket Name"),
)
price_gross = models.PositiveIntegerField(
verbose_name=_("Price gross"),
)
description = models.TextField(
null=True,
blank=True,
)
start_at = models.DateTimeField(
null=True,
blank=True,
verbose_name=_("Ticket sale starts at"),
)
end_at = models.DateTimeField(
null=True,
blank=True,
verbose_name=_("Ticket sale ends at"),
)
quantity = models.PositiveIntegerField(
verbose_name=_("Quantity"),
)
status = models.CharField(
max_length=8,
choices=TicketStatus.CHOICES,
default=TicketStatus.ON_SALE,
verbose_name=_("Status"),
)
In order to get the on_sale_soon field on your TicketQuerySet, you might need annotation such as
from datetime import date
from django.db import models
from django.db.models import Q, Value
tickets = Ticket.objects.annotate(
on_sale_soon=Case(
When(start_at__lte=date.today() + datetime.timedelta(days=10), then=Value(True, models.BooleanField())),
default=Value(False, models.BooleanField()),
output_field=models.BooleanField()
)
)
)
Afterwards, if you want to get tickets that are on sale soon, but not yet, you can do
tickets = tickets.objects.exclude(status=TicketStatus.ON_SALE)
tickets = tickets.filter(on_sale_soon=True)
Advantage of this is that everything will be done on the DB server in a single query.
Related
I have an Invoice query set. The invoice has an appointment object, that has a patient object that has a name attribute. I am trying to annotate so I can get the name of the patient as well. The rest of the fields return data okay. But the patient name doesn't. I have tried - F("appointment__patient__name") and also "appointment__patient__name". What am I missing here ?
def get_corporate_invoices(
start_date: date, end_date: date, corporate_uuid: str
) -> QuerySet[Invoice]:
return (
Invoice.objects.annotate(
entry_date=F("appointment_date"),
payment_method=Value("--", output_field=models.TextField()),
debit_amount=Round("amount", precision=2),
running_balance=Value(0.00, output_field=models.FloatField()),
patient_name=F("appointment__patient__name"),
)
.filter(
corporate_uuid=corporate_uuid,
appointment_date__gte=start_date,
appointment_date__lte=end_date,
isdeleted=False,
status="Cleared",
corporate_uuid__isnull=False,
)
.order_by("appointment_date", "concierge_rct_id")
.values(
"entry_date",
"payment_method",
"debit_amount",
"running_balance",
"patient_name"
)
)
models.py
class Invoice(models.Model):
transaction = models.ForeignKey(
Transaction,
on_delete=models.SET_NULL,
related_name="transaction_invoice",
blank=True,
null=True,
)
status = models.CharField(
max_length=25, choices=invoice_status_choices, default="Pending"
)
concierge_reference = models.TextField(blank=False, null=False)
concierge_rct_id = models.IntegerField(blank=False, null=True, unique=True)
appointment = models.JSONField(blank=False, null=True)
appointment_date = models.DateField(blank=False, null=True)
amount = models.FloatField(null=True, blank=True, default=0.00)
payment_mode = models.JSONField(blank=False, null=True)
This is what worked:
patient_name=Func(
F('appointment'), Value('patient'), Value('name'),
function='jsonb_extract_path_text'),
)
This is the full task :
Export a list of customers who completed registration but haven't performed any action (no invoice, expense, withdrawal) last week (3-9 May)
I need to create this type of SQL, but I don't know how to check for actions, what I did for now is
SELECT user FROM users_user
WHERE completed_registration=False
AND date_joined BETWEEN '2021-05-03 00:00:00' AND '2021-05-29 00:00:00'
UNION
SELECT user FROM invoice_invoice;
Check for users who had completed the registration, check for the date, and then check the invoice. But as I check for invoice_invoice itself it's an empty table, why do I get one user when I launch this query? The completed_registration and the date fields which are in queryset right now are only for test.
Only users
This is when I check only for invoices
This is the structure:
Expense model:
class Merchant(BaseModel):
company = models.ForeignKey(Company, on_delete=models.PROTECT, related_name='merchants')
name = models.CharField(max_length=255)
company_code = models.CharField(max_length=255, default='', blank=True)
def __str__(self):
return f'{self.company} {self.name}'
class Expense(Operation):
category = models.CharField(choices=ExpenseCategories.get_choices(), default=ExpenseCategories.GENERAL.name,
db_index=True, blank=True, max_length=255)
merchant = models.ForeignKey(Merchant, on_delete=models.PROTECT, related_name='expenses', blank=True, null=True)
amount = models.PositiveIntegerField(default=0, blank=True, help_text='Only taxable amount. In cents')
full_amount = models.PositiveIntegerField(
default=0,
blank=True,
help_text='Full amount. Most of the time same as amount or bigger. In cents'
)
currency = models.ForeignKey(Currency, on_delete=models.PROTECT, related_name='expenses',
default=settings.DEFAULT_CURRENCY_CODE)
description = models.TextField(default='', blank=True)
is_taxable = models.BooleanField(blank=True, default=True)
from_date = models.DateField(null=True, blank=True, help_text='Start date in case of aggregated bulk creation.')
to_date = models.DateField(null=True, blank=True, help_text='End date in case of aggregated bulk creation.')
receipt_number = models.CharField(blank=True, default='', max_length=255, help_text='Number from receipt.')
Invoice model:
class Invoice(Operation):
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, related_name='invoices')
number = models.CharField(max_length=255)
notes = models.TextField(default='', blank=True)
payment_due = models.DateField()
total = models.PositiveIntegerField(help_text='In cents', default=0)
payment_status = models.CharField(choices=InvoiceStatuses.get_choices(), default=InvoiceStatuses.UNPAID,
max_length=20)
pdf = models.FileField(null=True, blank=True, upload_to='invoices/pdf', max_length=255)
is_sent = models.BooleanField(default=False, help_text="Is pdf invoice sent")
User model:
class User(AbstractUser):
username = None
email = models.EmailField('email address', blank=True)
phone = PhoneNumberField(unique=True)
is_verified = models.BooleanField(default=False)
language = models.ForeignKey(
Language,
default=settings.DEFAULT_LANGUAGE,
on_delete=models.SET_DEFAULT,
)
avatar = models.ImageField(upload_to='users/avatars', null=True, blank=True)
companies = models.ManyToManyField(Company, related_name='users')
active_company = models.OneToOneField(Company, null=True, related_name='active_user', on_delete=models.SET_NULL)
agreement_text = models.TextField(default='', blank=True)
agreement_date = models.DateField(null=True, blank=True)
personal_no = models.CharField(max_length=100, default='', blank=True)
full_name = models.CharField(max_length=255, help_text='Field holds first and last names.', default='', blank=True)
completed_registration = models.BooleanField(default=False)
work_hours_from = models.TimeField(default=settings.DEFAULT_WORK_HOURS_FROM, null=True, blank=True)
work_hours_until = models.TimeField(default=settings.DEFAULT_WORK_HOURS_UNTIL, null=True, blank=True)
You seem to want not exists. I would expect logic like this:
SELECT u.*
FROM users_user u
WHERE u.completed_registration AND
NOT EXISTS (SELECT 1
FROM invoice_invoice i
WHERE i.user = u.user AND
i.invoice_date >= '2021-05-03' AND
i.invoice_date < '2021-05-10'
);
You would repeat this logic for each table where you want to check an action. Also, it is not clear what date you want to use within the invoice table, so I made one up.
I have three models, BoletoConfig(BilletConfig),Tickets and CategoryTicket, one ticket has a CategoryTicket and CategoryTicket has a BilletConfig, BilletConfig has an attribute with the days for the billet due, I want to create a method in the tickets class to calculate the due date.
I have doubts if I use the decorator #property or #classmethod, which would be the best choice and why? and how would i get the value of days_to_become_due from my BoletoConfig class in tickets?
This my /billets/models.BoletoConfig
class BoletoConfig(models.Model):
base_amount = models.DecimalField(
db_column='valor_base',
max_digits=10,
decimal_places=2,
verbose_name='valor base do boleto',
)
check_specialization_for_amount = models.BooleanField(
default=False,
db_column='especializacao_protocolo',
verbose_name='especialização do protocolo',
)
days_to_become_due = models.IntegerField(
db_column='dias_vencimento',
verbose_name='dias até vencimento',
)
class Meta:
db_table = 'boletos_boleto_configuracao'
verbose_name = 'boletos_configuração'
def __str__(self):
return self.base_amount
This my /tickets/models.tickets
class Ticket(TimestampedModel, permissions.TicketPermission):
"""A ticket requested by someone to address a situation"""
requested_by = models.ForeignKey(
settings.AUTH_USER_MODEL,
models.PROTECT,
db_column='requerido_por',
related_name='+',
)
show_ticket_for_request_user = models.BooleanField(
default=True,
db_column='mostrar_ticket_para_requerente',
)
message = models.CharField(
max_length=4000,
blank=True,
null=True,
db_column='mensagem',
)
status = models.ForeignKey(
Status,
models.PROTECT,
)
category = models.ForeignKey(
Category,
models.PROTECT,
)
files = models.CharField(max_length=4000, blank=True, null=True)
boleto_id = models.UUIDField(
db_column='boleto_id',
verbose_name='Uuid Boleto',
null=True,
blank=True,
)
#classmethod
def get_boleto_duo_date(self, category):
days_to_due = 'category__boleto_config__days_to_become_due'
return self.ticket.created_at + timedelta(days=days_to_due)
This my /tickets/models.CategoryTicket
class Category(TimestampedModel):
"""
Represents what the ticket is about.
EX: Change lesson attendance, change activity grade.
"""
.
.
.
.
boleto_config = models.ForeignKey(
BoletoConfig,
models.PROTECT,
db_column='boleto_config_id',
verbose_name='configuração de boleto',
null=True,
blank=True,
)
I expect to receive a var contact_exists that I can use to update some fields. However, the following query always gives me back django.core.exceptions.FieldError: Related Field got invalid lookup: event
Do you have any idea why event_related_fields__event doesn't work the way I expected?
for selected_order in Order.objects.all():
contact_exists = Contact.objects.filter(
event_related_fields__event=selected_order.event,
)
Here my models.py:
class Contact(TimeStampedModel):
consent = models.BooleanField(verbose_name=_("Consent"))
email = models.EmailField(verbose_name=_("Your email"))
first_name = models.CharField(
max_length=100, # TODO Length must be same as for billing model
verbose_name=_("First name"),
null=True,
blank=True,
)
last_name = models.CharField(
max_length=100, # TODO Length must be same as for billing model
verbose_name=_("Last name"),
null=True,
blank=True,
)
events = models.ManyToManyField(Event, related_name='contacts')
event_related_fields = models.ManyToManyField(
Event, related_name='event_related_fields', through='EventRelatedFields'
)
organizer = models.ForeignKey(
Organizer, on_delete=models.PROTECT, related_name='contacts'
) # PROTECT = don't allow to delete the organizer if contact exists
class Meta:
verbose_name = _("Contact")
verbose_name_plural = _("Contacts")
ordering = ('created',)
unique_together = ('email', 'organizer')
def __repr__(self):
return "{}: {}".format(self.__class__.__name__, self)
def __str__(self):
return self.email
class EventRelatedFields(TimeStampedModel):
event = models.ForeignKey(Event, on_delete=models.CASCADE)
contact = models.ForeignKey(Contact, on_delete=models.CASCADE)
lead = models.BooleanField(
verbose_name='Lead', default=False
) # Contact who 'Signed Up'
attendee = models.BooleanField(
verbose_name='Attendee', default=False
) # Contact assigned to ticket
purchaser = models.BooleanField(
verbose_name='Purchaser', default=False
) # Contact made the order
class Meta:
unique_together = [['event', 'contact']]
You don't need the __event lookup, try using:
for selected_order in Order.objects.all():
contact_exists = Contact.objects.filter(
event_related_fields=selected_order.event,
)
The lookup part should contain field names of Event model.
I have two table and want to releated these with one-to-many.
One unity - many stat.
I try to select field but it not working.
Always my ForeignKey is Unit.name.
I want to use Unit.id as ForeignKey !!
I thought that the Unit.id can't be so for tests I tried another field Unit.name2, it also doesn't work.
https://docs.djangoproject.com/en/2.0/ref/models/fields/#django.db.models.ForeignKey.to_field
class Unit(models.Model) :
id = models.AutoField(primary_key=True)
name = models.CharField( max_length=20, unique=True)
name2 = models.CharField( max_length=20, unique=True)
rase = models.CharField( max_length=10 )
class Stat(models.Model) :
id = models.AutoField(primary_key=True)
unit_id = models.ForeignKey( Unit, to_field='name2', on_delete=models.CASCADE, verbose_name='unit_id',)
attack_type = models.CharField( 'attack_type', max_length=10 )
attack_speed = models.DecimalField( 'attack_speed', max_digits=5, decimal_places=4 )
attack_number = models.IntegerField( 'attack_number' )
Additional question.
If I left Unit.name as ForeignKey what happen when i change a Unit.name in the future? If name value will change in all related tables?
That's why I want to use Unit.id as a ForeignKey because Unit.id will not change but the Unit.name can.
I added unique=True also to AutoField in Unit model.
Also I change Unit.id to Unit.unit_id.
It seems it working.
class Unit(models.Model) :
unit_id = models.AutoField(primary_key=True, unique=True, verbose_name='unit_id', )
name = models.CharField( max_length=20, unique=True)
name2 = models.CharField( max_length=20, unique=True)
rase = models.CharField( max_length=10 )
def __str__( self ) :
string = 'unit_id: {} - {}'.format(self.unit_id, self.name)
return string
class Stat(models.Model) :
stat_id = models.AutoField(primary_key=True)
unit_id = models.ForeignKey( Unit, to_field='unit_id', on_delete=models.CASCADE, verbose_name='unit_id', )
attack_type = models.CharField( 'attack_type', max_length=10 )
attack_speed = models.DecimalField( 'attack_speed', max_digits=5, decimal_places=4 )
attack_number = models.IntegerField( 'attack_number' )
def __str__( self ) :
string = '{0} {1} stat'.format(self.unit_id, self.attack_type)
return string