I have following query that I ran to create a database view inside my SQLite database:
CREATE VIEW customerview AS
SELECT
a.id
, name
, email
, vat
, street
, number
, postal
, city
, country
, geo_lat
, geo_lon
, customer_id
, is_primary
FROM customerbin_address a
, customerbin_customer b
WHERE b.id = a.customer_id
AND a.is_primary = 1
In models.py I added the model:
class Customerview(models.Model):
name = models.CharField(max_length=100, db_column='name')
email = models.EmailField(unique=True, db_column='email')
vat = VATNumberField(countries=['NL', 'BE', 'FR', 'DE', 'UK'], blank=True, null=True, db_column='vat')
street = models.CharField(max_length=100, db_column='street')
number = models.IntegerField(null=True, db_column='number')
postal = models.IntegerField(null=True, db_column='postal')
city = models.CharField(max_length=100, db_column='city')
country = CountryField(db_column='country')
is_primary = models.BooleanField(null=False, db_column='is_primary')
geo_lat = models.DecimalField(max_digits=9, decimal_places=6, blank=True, null=True, db_column='geo_lat')
geo_lon = models.DecimalField(max_digits=9, decimal_places=6, blank=True, null=True, db_column='geo_lon')
class Meta:
managed = False
db_table = 'customerview'
and in admin.py I altered the list:
#admin.register(models.Customerview)
class CustomerviewAdmin(admin.ModelAdmin):
list_display = ('name', 'email', 'vat', 'street', 'number', 'postal', 'city', 'country', 'is_primary', 'geo_lat', 'geo_lon')
readonly_fields = ('name', 'email', 'vat', 'street', 'number', 'postal', 'city', 'country', 'is_primary', 'geo_lat', 'geo_lon',)
How do I programatically add the database view with the query above in my application?
Django's migrations framework lets you execute raw SQL - https://docs.djangoproject.com/en/3.1/ref/migration-operations/#runsql
So, you could create an empty migration (manage.py makemigrations <appname> --empty) and then edit it to execute your view-creating SQL via a migrations.RunSQL() call.
Maybe you should try this with the get_view_str method https://pypi.org/project/django-database-view/#description
Related
I have a model like this:
class Cart(models.Model):
id = models.UUIDField(primary_key=True, default=uuid4)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE, null=True)
class Reception(models.Model):
PAYMENT_STATUS_PENDING = 'P'
PAYMENT_STATUS_COMPLETE = 'C'
PAYMENT_STATUS_FAILED = 'F'
PAYMENT_STATUS_CHOICES = [
(PAYMENT_STATUS_PENDING, 'Pending'),
(PAYMENT_STATUS_COMPLETE, 'Complete'),
(PAYMENT_STATUS_FAILED, 'Failed')
]
cart = models.ForeignKey(Cart, on_delete=models.CASCADE, null=True)
customer = models.ForeignKey(Customer, on_delete=models.CASCADE)
entry_date = models.DateField()
total_price = models.IntegerField()
payment_status = models.CharField(
max_length=1,
choices=PAYMENT_STATUS_CHOICES,
default=PAYMENT_STATUS_PENDING
)
My question is:
How can I get a particular Cart record from the Reception model?
I tried using this serializer:
class ReceptionSerializer(serializers.ModelSerializer):
class Meta:
model = Reception
fields = ['id', 'customer', 'entry_date', 'payment_status', 'cart']
but it only returns the id of a cart. I want to return the whole object of that specific cart.
How can I do that?
If you want to utilize nested serialization, one possible solution would be to define CartSerializer and override the cart field of the ReceptionSerializer class as follows
class CartSerializer(serializers.ModelSerializer):
class Meta:
model = Cart
fields = ['id', 'customer']
class ReceptionSerializer(serializers.ModelSerializer):
cart = CartSerializer(many=True)
class Meta:
model = Reception
fields = ['id', 'customer', 'entry_date', 'payment_status', 'cart']
However, please note that the cart field will become read-only this way.
I have two models Bookings and Presenter, which are setup as a Many to Many Field.
I then have a function called 'export excel' (found on online tutorial) , which exports all of the data from the Booking model to an Excel spreadsheet. Each booking may contain more than one presenter.
Currently the export works but displays a new record for bookings that have more than one presenter assigned. Is it possible to get the presenter name be displayed as a list? rather than duplicating the booking on a new row.
So for example if booking 1 contains two presenters the names will display as ['mark', 'jake']
Queryset used in export excel function
rows= Bookings.objects.all().exclude(status='Cancelled').values_list(
'id', 'program_type', 'delivery_method', 'booking_date', 'booking_time', 'duration', 'school_name', 'year_level', 'street', 'suburb', 'post_code',
'contact_name', 'email', 'phone_number', 'comments', 'students_attending', 'status', 'presenter__name')
Models
class Presenter(models.Model):
id = models.BigAutoField(primary_key=True)
name = models.CharField(max_length=80)
email = models.EmailField()
phone_number = models.CharField(max_length=10)
role = models.CharField(max_length=50)
def __str__(self):
return self.name
class Bookings(models.Model):
statuses = [
('TBC', 'TBC'),
('Confirmed', 'Confirmed'),
('Completed', 'Completed'),
('Cancelled', 'Cancelled'),
]
id = models.BigAutoField(primary_key=True)
program_type = models.CharField(max_length=40)
delivery_method = models.CharField(max_length=40)
booking_date = models.DateField()
booking_time = models.CharField(max_length=10)
duration = models.CharField(max_length=10)
school_name = models.CharField(max_length=120)
year_level = models.CharField(max_length=10)
street = models.CharField(max_length=120)
suburb = models.CharField(max_length=120)
post_code = models.CharField(max_length=10)
contact_name = models.CharField(max_length=80)
email = models.EmailField()
phone_number = models.CharField(max_length=10)
comments = models.TextField(blank=True, null=True)
students_attending = models.CharField(max_length=5)
status = models.CharField(max_length=80, choices=statuses, default='TBC')
presenter = models.ManyToManyField(Presenter)
def __str__(self):
return self.contact_name
Function
def export_excel(request):
# Set the application type
response = HttpResponse(content_type='application/ms-excel')
# Set the file name and extenstion
response['Content-Disposition'] = 'attachment; filename=Advancement Series' +'.xls'
# Create the workbook
wb = xlwt.Workbook(encoding='utf=8')
# Add the worksheets
advancement = wb.add_sheet('Advancement')
# Set the starting row number
advancement_row_num = 0
# Set the font style
font_style = xlwt.XFStyle()
font_style.font.bold = True
font_style.font.name = 'calibri'
# Set the worksheet columns
advancement_columns = ['Booking ID', 'Program Type', 'Delivery Method', 'Booking Date', 'Booking Time', 'Duration', 'School Name', 'Year Level', 'Street', 'Suburb','Post Code', 'Contact Name', 'Email', 'Phone Number', 'Comments', ' Students Attending', 'Status', 'Presenter']
for col_num in range(len(advancement_columns)):
advancement.write(advancement_row_num, col_num, advancement_columns[col_num], font_style)
# Set the font style for non headings
font_style = xlwt.XFStyle()
font_style.font.name = 'calibri'
# Adds the values from the Bookings table
rows= Bookings.objects.all().exclude(status='Cancelled').values_list(
'id', 'program_type', 'delivery_method', 'booking_date', 'booking_time', 'duration', 'school_name', 'year_level', 'street', 'suburb', 'post_code',
'contact_name', 'email', 'phone_number', 'comments', 'students_attending', 'status', 'presenter__name')
for row in rows:
advancement_row_num+=1
for col_num in range(len(row)):
advancement.write(advancement_row_num, col_num, str(row[col_num]), font_style)
wb.save(response)
return response
Managed to resolve this using the Django import-export.
https://django-import-export.readthedocs.io/en/latest/getting_started.html
I was asked to add some logic to model uniqueness.
Each Payment must have either transaction_id or payment_id filled.
Each Payment is identificated by (transaction_id, operation, payment_created_date) or (payment_id, operation, payment_created_date).
On database level this works fine.
Inserting Payment with same transaction_id, operation, payment_created_date twice causes unique constraint violation.
For this model i created admin page. But inserting same row with admin page causes IntegrityError at /admin/finance/payment/add/ duplicate key value violates unique constraint "unique_finance_payment_without_payment_id" DETAIL: Key (transaction_id, operation, payment_created_date)=(dasdasd, Refund, 2021-10-04) already exists. instead of simple user-friendly admin error Please correct the error below. Payment with this Transaction id, Operation and Payment created date already exists. How to make Django admin catch this IntegrityError and show it in admin form?
here is my models.py
class ReportName(DiModel):
name = CharField(
max_length=50,
blank=False,
null=False)
def __str__(self):
return self.name
class Payment(DiModel):
class Meta:
unique_together = ('transaction_id', 'payment_id', 'operation', 'payment_created_date')
constraints=[UniqueConstraint(fields=['transaction_id', 'operation', 'payment_created_date'],
condition=Q(payment_id=None),
name='unique_finance_payment_without_payment_id'),
UniqueConstraint(fields=['payment_id', 'operation', 'payment_created_date'],
condition=Q(transaction_id=None),
name='unique_finance_payment_without_transaction_id'),]
OPERATION_TYPE = [
('Refund', 'Refund'),
('Charge', 'Charge'),
]
CURRENCY_CODE = [
('EUR', 'EUR'),
('RUB', 'RUB'),
('USD', 'USD'),
('GBP', 'GBP'),
('AUD', 'AUD'),
('PLN', 'PLN'),
('SGD', 'SGD'),
('MYR', 'MYR'),
('RON', 'RON'),
('ZAR', 'ZAR'),
]
report_name = ForeignKey(ReportName,
on_delete=models.PROTECT,
blank=False,
null=False,
help_text="Processor and report type")
operation = CharField(max_length=6,
choices=OPERATION_TYPE,
blank=False,
null=False,
default=OPERATION_TYPE[0][0],
help_text='Payment operation type')
payment_created_date = DateField(blank=False,
null=False,
default=timezone.now)
amount = DecimalField(blank=True,
null=True,
max_digits=10,
decimal_places=2,
help_text='Payment amount')
commission = DecimalField(blank=False,
null=False,
max_digits=10,
decimal_places=2,
help_text='Transaction fee')
currency = CharField(max_length=3,
choices=CURRENCY_CODE,
blank=False,
null=False,
default=CURRENCY_CODE[0][0],
help_text='Amount and commission currency code')
transaction_id = CharField(max_length=32,
blank=True,
null=True,
help_text='Payota transaction id')
payment_id = IntegerField(blank=True,
null=True,
help_text='Payota payment id')
author_user = models.ForeignKey(User,
on_delete=models.PROTECT,
null=True,
related_name='finance_author_user')
def __str__(self):
return f"{self.report_name} {self.operation} {self.created_at}"
and this is my admin.py
class PaymentForm(forms.ModelForm):
class Meta:
fields = (
'report_name',
'operation',
'payment_created_date',
'amount',
'commission',
'currency',
'transaction_id',
'payment_id',
)
#admin.register(Payment)
class PaymentAdmin(ImportExportMixin, admin.ModelAdmin):
form = PaymentForm
list_display = [
'report_name',
'operation',
'payment_created_date',
'amount',
'commission',
'currency',
'transaction_id',
'payment_id',
]
list_filter = (
'report_name',
'operation',
('payment_created_date', DateRangeFilter),
)
search_fields = ['report_name__name', 'operation']
resource_class = PaymentResource
class Meta:
model = Payment
I can't get "list_display" to display field from related table.
models.py
class product(models.Model):
product_id = models.AutoField(primary_key=True)
EAN = models.CharField(unique=True, editable=False, max_length=13)
Product_name = models.CharField(max_length=50)
class price(models.Model):
price_id = models.AutoField(primary_key=True)
EAN = models.ForeignKey(product, to_field="EAN", on_delete=models.CASCADE)
Vendor = models.ForeignKey(vendor, to_field="Vendor_name", on_delete=models.CASCADE)
Qty = models.CharField(max_length=15)
Price = models.DecimalField(max_digits=8, decimal_places=2, null=True)
panels = [
FieldPanel('EAN'),
FieldPanel('Vendor'),
FieldPanel('Qty'),
FieldPanel('Price'),
]
hooks.py
class price_admin(ModelAdmin):
model = pricelist
menu_label = 'price'
menu_icon = 'pilcrow'
menu_order = 300
add_to_settings_menu = False
exclude_from_explorer = False
list_display = ('EAN_id', 'Vendor_id', 'Price') # <-Here I have a problem
list_filter = ('Vendor_id__Name',)
search_fields = ('Vendor_id__Name', 'EAN_id__EAN')
I'm able to get "Vendor_id__Name" to work in "list_filter" and "search_fields", but when I put "Vendor_id__Name" to list_display, I get this error:
AttributeError: Unable to lookup 'EAN_id__Product_name' on price or price_admin
So, what is the right way to display field(Vendor_id__Name in my case) from related table?
Any help would be really appreciated!!
As Ivan Starostin noticed you have a typo in related field name. Other option you can use - a method field or basically - a callable that list display do accept:
class price_admin(ModelAdmin):
...
list_display = ('vendor_name', # other fields)
def vendor_name(self, obj):
return obj.EAN.Product_name
vendor_name.short_description = 'Vendor name'
I want to change field names. In my model field name start with prefix timesheet. And when i am using api i have to use that timesheet prefix. I want to remove that prefix instead keep jobs, clock_in_date, clock_out_date.... How can i rename field names so that when i am send data from api body should contain names without timesheet prefix
class TimesheetSerializer(serializers.ModelSerializer):
timesheet_hours = TimesheetHourSerializer(many=True, read_only=True)
class Meta:
model = TimesheetEntry
fields = [
'id',
'timesheet_jobs',
'timesheet_clock_in_date',
'timesheet_clock_in_time',
'timesheet_clock_out_date',
'timesheet_clock_out_time',
'timesheet_note',
'timesheet_hours',
]
Models.py
class TimesheetEntry(models.Model):
timesheet_users = models.ForeignKey(User, on_delete=models.CASCADE,related_name='timesheet_users')
timesheet_jobs = models.ForeignKey(Jobs, on_delete=models.CASCADE,related_name='timesheet_jobs', blank=True, null=True)
timesheet_clock_in_date = models.DateField()
timesheet_clock_in_time = models.TimeField()
timesheet_clock_on = models.DateTimeField(auto_now_add=True)
timesheet_clock_in_by = models.ForeignKey(User, on_delete=models.CASCADE,related_name='timesheet_user_clock_in_by')
timesheet_clock_out_date = models.DateField(blank=True, null=True)
timesheet_clock_out_time = models.TimeField(blank=True, null=True)
class TimesheetSerializer(serializers.ModelSerializer):
timesheet_hours = TimesheetHourSerializer(many=True, read_only=True)
jobs = serializers.CharField(source='timesheet_jobs')
class Meta:
model = TimesheetEntry
fields = [
'id',
'jobs',
.......
]
you can simply use this. This would work for write operations also