I'm converting an ancient Client/Server app (Delphi) into a Django app for a small brick and morter bookstore (wife's).
Since most functions are admin, I'm using the Django admin interface with grappelli for some easier lookups.
I have 3 models: Book, Sale and Item.
class Book(TimeStampedModel):
"""
Books are described individually and are related to collections
as many to many. Every book in this system is unique - i.e. there are
not quantity fields. This is optimized for used book stores where book
condition is essential.
"""
STATUS_CHOICES = (
('IN STOCK', 'IN STOCK'),
('SOLD', 'SOLD'),
('ON LOAN', 'ON LOAN'),
('HOLD', 'HOLD'),
)
isbn = models.CharField(max_length=50, blank=True)
title = models.CharField(max_length=200, blank=False, db_index=True)
author = models.CharField(max_length=200, blank=True)
sell_price = models.DecimalField(max_digits=10,decimal_places=2, default=0)
description = models.TextField()
collections = models.ManyToManyField(Collection)
class Meta:
index_together = [
["author", "title"],
["status", "title", "author"],
]
def __unicode__(self):
return "%s [%d] - %s - $%.2f" % (self.title, self.id, self.book_type, self.sell_price)
#staticmethod
def autocomplete_queryset():
instock = Book.objects.filter(status="IN STOCK")
return instock
#staticmethod
def autocomplete_search_fields():
return("id__iexact", "title__istartswith",)
class Sale(TimeStampedModel):
"""
Sales group all sold items which may or may not be books and are sold to contacts.
We use a "generic" contact of "cash" for non named contacts
"""
PAYMENT_TYPE_CHOICES = ( ('Cash', 'Cash'), ('Charge', 'Charge'), ('Check', 'Check'))
contact = models.ForeignKey(Contact, null=True)
sale_date = models.DateField(blank=True,default=datetime.date.today, db_index=True)
payment_type = models.CharField(max_length=50, choices=PAYMENT_TYPE_CHOICES)
taxed = models.BooleanField(default=True)
tax_exempt_no = models.CharField(blank=True, max_length=50)
sales_tax = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
amt_tender = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
pct_discount = models.SmallIntegerField(blank=True, null=True)
amt_credit = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
amt_shipping = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
amt_due = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
tot_sale = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
tot_items = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
ordering = ['-sale_date']
def __unicode__(self):
return str(self.sale_date)
class Item(TimeStampedModel):
"""
Items are usually books sold on a sale. Items can also be entered manually
at time of sale if they are not books from inventory
"""
sale = models.ForeignKey(Sale)
book = models.ForeignKey(Book, null=True, blank=True)
item_desc = models.CharField(blank=True, max_length=200)
cost = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
sell_price = models.DecimalField(max_digits=10, decimal_places=2, blank=True, null=True)
def __unicode__(self):
return self.item_desc
For the Sale form, I use an admin form with Tabular inline Items. Items are usually books (via a foreign key lookup), but can also be entered manually for non-inventory items so I have a sell_price both in the book model and in the item model.
class ItemInline(admin.TabularInline):
model = Item
raw_id_fields = ("book",)
autocomplete_lookup_fields = {
'fk': ['book'],
}
extra = 2
What I'd like to do in the foreign key lookup is to return the key of the book AND fill in the Item's sellprice with the sellprice from the book I looked up.
I have the basic lookup working just fine but can't find out how to set the item sellprice to the book's sellprice immediately after the lookup.
Any advice is appreciated! I have tried figuring out the objects to put some JS logic in but the inlines get object ids created dynamically, I think. I'm no JS expert.
Related
I have a model called Actual:
# Actual parts table
class Actual(models.Model):
vendor = models.ForeignKey(Vendor, on_delete=models.CASCADE, verbose_name="Vendor name", related_name="actualvendor")
number = models.CharField("External part number", max_length=32, unique=True, blank=True, null=True)
description = models.CharField(max_length=64, blank=True)
pq = models.DecimalField(max_digits=7, decimal_places=2, default=1)
mrrp = models.DecimalField(max_digits=10, decimal_places=2)
# Model metadata
class Meta:
unique_together = ["vendor", "number"]
verbose_name_plural = "actual external parts"
# Display below in admin
def __str__(self):
return f"{self.number}"
I also have another model called Offer:
class Offer(models.Model):
sync_id = models.ForeignKey(Sync, on_delete=models.CASCADE, verbose_name="Internal part number", related_name="part")
discount = models.DecimalField(max_digits=3, decimal_places=2, default=0)
moq = models.DecimalField(max_digits=4, decimal_places=2, default=1)
status = models.CharField(max_length=20, choices=OFFERSTATUS_CHOICES, default=1)
actual = models.OneToOneField(Actual, on_delete=models.CASCADE)
# Display something in admin
def __str__(self):
return f"Offer {self.id} for {self.sync_id}"
# Calculate the price
def price(self):
return self.actual.mrrp * (1-self.discount)
I am trying to calculate the 'price' in the 'Offer' model using 'mrrp' but 'mrrp' is from the 'Actual' model.
I am able to do so with the code I've attached but as you can see in the django admin, the 'actual' shows up as a field. I don't want it to appear as a field. I just want 'actual' to be a variable that is equal to the value of 'mrrp'. That way I can use it to calculate the price.
Is there another way to reference fields from another model? Surely fields aren't the only way?
I have a model like
class tbl_payment(models.Model):
document_id = models.ForeignKey(tbl_invoice, on_delete=models.CASCADE)
client_id = models.ForeignKey(tbl_customer, on_delete=models.CASCADE)
total_amount = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
paid_amount = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
balance = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
date = models.DateField(blank=True, null=True)
status = models.CharField(max_length=50)
Now what I want to do is whenever a new record is added, or an existing record changes, balance should be updated as the difference between total_amount and paid_amount (simple maths), and based on my balance column, I want to save status as Paid, Partial or Unpaid.
I want to refrain from calculating the balance in my views and then saving in the database, instead, I want to handover this part to my models so that my models take care of the balance and I may avoid errors which I am subconsciously afraid of.
I came to know that this is done something like this
class tbl_payment(models.Model):
document_id = models.ForeignKey(tbl_invoice, on_delete=models.CASCADE)
client_id = models.ForeignKey(tbl_customer, on_delete=models.CASCADE)
total_amount = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
paid_amount = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
balance = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
date = models.DateField(blank=True, null=True)
status = models.CharField(max_length=50)
#property
def balance(self, value):
return self.total_amount - paid_amount
but what should I pass in place of value??
also when I try to get due_amount value like this
tbl_bulk_payment.objects.get(pk=1).due_amount
it gives due_amount() missing 1 required positional argument: 'value'
what is the correct way of doing this??
you have to override save() function
class tbl_payment(models.Model):
class Status(models.TextChoices):
paid = 'Paid', 'Paid'
partial = 'Partial', 'Partial'
unpaid = 'Unpaid', 'Unpaid'
document_id = models.ForeignKey(tbl_invoice, on_delete=models.CASCADE)
client_id = models.ForeignKey(tbl_customer, on_delete=models.CASCADE)
total_amount = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
paid_amount = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
balance = models.DecimalField(max_digits=8, decimal_places=2, blank=True, null=True)
date = models.DateField(blank=True, null=True)
status = models.CharField(max_length=50, choices=Status.choices)
def save(self, *args, **kwargs):
self.balance = self.total_amount - self.paid_amount
if self.balance >= 0:
self.status = Status.paid
elif self.paid_amount == 0:
self.status = Status.unpaid
else:
self.status = Status.partial
super(tbl_payment, self).save(*args, **kwargs)
P.S. your Model class name is not following the python or Django naming rule.
class Payment(models.Model) would be better.
and document_id => document. because
payment = Payment.objects.get(pk=1)
payment.document_id
in this case payment.document_id would return document instance. (not document instance's id). so document is more Django like style than document_id
** I just need one more table join in my query **
I want to get sales of logged-in users with order detail and shipping address.
I am getting sales of current user through this query but i also want get shipping address.
orderitems = OrderItem.objects.filter(
product__user=request.user, order__complete=1).order_by('-date_orderd')
Now i want to get also address, city and state from the Shippingaddress model.
I attached the models below.
this is my current result.
My models:
Order Model:
class Order(models.Model):
user = models.ForeignKey(
User, on_delete=models.CASCADE, blank=True, null=True)
date_orderd = models.DateTimeField(auto_now_add=True)
complete = models.BooleanField(default=False, null=True, blank=False)
transaction_id = models.CharField(max_length=200, null=True)
# product = models.ManyToManyField(OrderItem)
def __str__(self):
return str(self.id)
Order items Model:
class OrderItem(models.Model):
product = models.ForeignKey(
Product, on_delete=models.CASCADE, blank=True, null=True)
order = models.ForeignKey(
Order, on_delete=models.SET_NULL, blank=True, null=True)
quantity = models.IntegerField(default=0, null=True, blank=True)
date_orderd = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(
User, on_delete=models.SET_NULL, blank=True, null=True)
price = models.FloatField(blank=True, null=True)
def __str__(self):
return str(self.product)
Shipping Address Model:
class ShippingAddress(models.Model):
user = models.ForeignKey(
User, on_delete=models.CASCADE, blank=True, null=True)
order = models.ForeignKey(
Order, on_delete=models.CASCADE, blank=True, null=True)
name = models.CharField(max_length=150)
address = models.CharField(max_length=150)
city = models.CharField(max_length=150)
state = models.CharField(max_length=150)
zipcode = models.CharField(max_length=150)
date_orderd = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.address
What you are looking for is "Select from multiple tables in one query with Django". You can take a look at the answers here.
Why not add another query like the one below
shp_address = ShippingAddress.objects.filter(product__user=request.user)
and if needed send to the client side as part of context, see below
context = {
'orderitems': orderitems,
'shp_address': shp_address
}
I'm building a cart model with the following code.
from django.db import models
class Item(models.Model):
name = models.CharField(max_length=200)
price = models.DecimalField(max_digits=8, decimal_places=2)
def __str__(self):
return self.name
class Order(models.Model):
date = models.DateTimeField(auto_now_add=True)
transcation_id = models.CharField(max_length=200, null=True)
def __str__(self):
return str(self.date)
class OrderItem(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE, blank=True, null=True)
order = models.ForeignKey(Order, on_delete=models.CASCADE, blank=True, null=True)
quantity = models.IntegerField(default=0, blank=True, null=True)
The many-to-one relationship between Item and Order allows one Order to contain many Item and that looks fine.
A model instance can be simply cloned as already answer in this question.
My problem is, if the price of an Item is changed. The price of contained items in Order is change too. But I don't want it to be changed. In the situation that customer already make a purchase, the price cannot be change. Is there anyway to clone the Order instance that completely not related to the other model?
Save the price manually
class OrderItem(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE, blank=True, null=True)
order = models.ForeignKey(Order, on_delete=models.CASCADE, blank=True, null=True)
quantity = models.IntegerField(default=0, blank=True, null=True)
price = price = models.DecimalField(max_digits=8, decimal_places=2, null=True, default=None)
def save():
if self.pk == None:
self.price = self.item.price
super(OrderItem, self).save()
I have a table that displays a list of "leads" which are rendered fine. There is also a related model which is called "Leadupdate" that is related to "lead" model that is used in the table. There is a many to one relationship from Leadupdate to lead with a foreign key. I want to display all the related updates for the individual "leads" in one of the updates column. There are several examples online for following forward relationship through foreign key but haven't found one for reverse yet. Here is one example of said relationship Accessor forward look up.
EDIT: Look up will be done on a Django-tables2 module instance table. I am not asking reverse look up on a model but doing it in context of Django-tables2.
Models.py:
class lead(models.Model):
slug = models.SlugField(unique=True,blank=True, null=True)
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100, blank=True, null=True)
business_name = models.CharField(max_length=100,blank=True, null=True)
email = models.EmailField(max_length=75, blank=True, null=True)
phone_number = models.CharField(max_length=20, blank=True, null=True)
address = models.CharField(max_length=150, blank=True, null=True)
city = models.CharField(max_length=50, blank=True, null=True)
state = models.CharField(max_length=10, blank=True, null=True)
zipcode = models.CharField(max_length=5, blank=True, null=True)
submission_date = models.DateTimeField(auto_now_add=True, blank=True)
assigned_to = models.ManyToManyField(Listing,blank=True, null=True, related_name="leads")
requested_software = models.CharField(max_length=50, blank=True, null=True)
type_of_business = models.CharField(max_length=30, choices=TYPE_OF_BUSINESS, default='Bar', blank=True, null=True)
time_frame = models.CharField(max_length=10, choices=TIME_FRAME, default='1')
comments = models.TextField(blank=True, null=True)
def __unicode__(self):
return self.business_name
#models.permalink
def get_absolute_url(self):
return('listing_detail', (),{'slug' :self.slug,})
def save(self, *args, **kwargs):
if not self.slug:
self.slug = slugify(self.business_name)
super(lead, self).save(*args, **kwargs)
class Leadupdate(models.Model):
CONFIDENCE_LEVEL = (
('HOT', 'HOT'),
('COLD', 'COLD'),
)
LEAD_VALUE = (
('1K3K', '1K-3K'),
('5K10K', '5K-10K'),
('10K20K', '10K-20K'),
('20K50K', '20K-50K'),
('50KUP', '5OK-UP'),
)
ESTIMATED_CLOSING = (
('1w4w', '1-4 Weeks'),
('1m3m', '1-3 Months'),
('3m6m', '3-6 Months'),
('6m+', '6+ Months'),
)
updatedate = models.DateTimeField(auto_now_add=True)
update = models.TextField(blank=True, null=True)
updatefrom = models.ForeignKey(Listing, related_name="update_from", blank=True, null=True)
lead = models.ForeignKey(lead, related_name="related_update",blank=True, null=True)
lead_confidence_level = models.CharField(max_length=10, choices=CONFIDENCE_LEVEL, default='COLD', blank=True, null=True)
estimated_lead_value = models.CharField(max_length=10, choices=LEAD_VALUE, default='1K3K', blank=True, null=True)
estimated_closing_frame = models.CharField(max_length=10, choices=ESTIMATED_CLOSING, default='1-4 Weeks', blank=True, null=True)
def __unicode__(self):
return u" %s - %s " % (self.update, self.updatedate)
Table:
class LeadTable(tables.Table):
business_name = tables.LinkColumn('lead-detail', args=[A('slug')])
updates = tables.Column(accessor='lead.related_update')
class Meta:
model = lead
fields = ("business_name","first_name", "last_name","number_of_pos","submission_date","updates")
attrs = {"class":"paleblue"}
A late answer, but here is what works for me in Django 1.8.6 with django-tables2 1.1.0 (based on Django-Tables2 Issue 156 and This answer). To access a one to many set of objects via a foreign key relation you need to just use the related_name in the accessor and then create a render method to produce what gets written to column cell. In that method you can then get all the foreign model objects and access their fields in a for loop.
class LeadTable(tables.Table):
business_name = tables.LinkColumn('lead-detail', args=[A('slug')])
updates = tables.Column(accessor='related_update')
def render_updates(self, value, table):
updates = ""
uFirst = True
updatesList = list(value.all())
for u in updatesList:
if not uFirst:
updates += ", "
else:
uFirst = False
updates += u.update
return updates
class Meta:
model = lead
fields = ("business_name","first_name", "last_name","number_of_pos","submission_date","updates")
attrs = {"class":"paleblue"}
according to django docs
in your views you can access them in this way (assuming lead_instance is an instance of lead class):
all_leadtables_for_lead = lead_instance.leadtable_set
a side note: use Capitalized names for classes (class Lead(models.Model):) in order to adhere to python PEP8 guidelines.