Displaying "info" by selecting foreign key in django admin - python

I was introduced to Python and Django about two weeks ago, so bear with me. I should also say now that I am using Django 1.6 and Python 3.3.
My project is an order management system. Here's how it works: A customer comes into a store and orders an item. That item is put in an order, which is idle until the employee places the order with a supplier. Basically, I have an Order table, which collects information like customer_name, order_date, status, etc. Then I have an EmployeeOrder table (1-1 relationship with Order) which has all of the elements of an Order, but also takes in employee_placed and employee_order_date (pretty much just extends Order).
What I'm trying to do is write code so that when the user selects which customer Order is being fulfilled by placing an EmployeeOrder, that customer Order's information is displayed. I don't really care how it is displayed right now, a pop up will work just fine. I just don't know how to do it and I haven't come across it anywhere. Right now the drop down box just displays Order 1, Order 2, etc. The user isn't going to remember which items were included in which order, so that's why I want the order information to be shown. Anyway, here's my code so far:
models.py
class Order(models.Model):
customer_order_date = models.DateTimeField('Date helped')
customer_placed = models.ForeignKey(Customer)
employee_helped = models.ForeignKey(Employee)
STATUS_OF_ORDER = (
('IDLE', 'Not yet ordered'),
('SHIP', 'Awaiting delivery'),
('PICK', 'Ready for pickup'),
('UNAV', 'Unavailable for order'),
('BACK', 'Backordered - awaiting delivery'),
('CANC', 'Canceled by customer'),
('ARCH', 'Fulfilled - archived'),
)
status = models.CharField(max_length=4, choices=STATUS_OF_ORDER,
default='IDLE', editable=False)
paid = models.BooleanField('Paid', default=False)
ship = models.BooleanField('Ship', default=False)
comments = models.CharField(max_length=200, blank=True, null=True)
item = models.ManyToManyField(Item)
def __str__(self):
return 'Order ' + str(self.id)
def is_idle(self):
return self.status == 'IDLE'
class EmployeeOrder(models.Model):
order = models.OneToOneField(Order, primary_key=True,
limit_choices_to={'status': 'IDLE'})
employee_order_date = models.DateTimeField('Date ordered')
employee_placed = models.ForeignKey(Employee)
admin.py
class OrderAdmin(admin.ModelAdmin):
list_display = ('customer_order_date', 'customer_placed')
raw_id_fields = ('customer_placed', 'item')
class EmployeeOrderAdmin(admin.ModelAdmin):
list_display = ('employee_order_date', 'employee_placed')
Any and all help is appreciated as I still admit that I am a total noob when it comes to Python and Django!

It sounds to me like you want an employee to be able to use the admin site to create an employee order from a customer order. I think it could be as simple as adding a raw ID field for the customer order. That is, I think you can just change EmployeeOrderAdmin like so:
class EmployeeOrderAdmin(admin.ModelAdmin):
list_display = ('employee_order_date', 'employee_placed')
raw_id_fields = ('order',)
Now when an employee creates an employee order, they will be able to use the OrderAdmin page to find the order they want.
Additionally, suppose you want that pop-up window to display the orders in a particular way. In that case, keep in mind that requests to display that pop-up window will contain an additional GET parameter called pop. You could:
class OrderAdmin(admin.ModelAdmin):
def queryset(self, request):
qs = super(ActiveOfferAdmin, self).queryset(request)
if request.GET.get('pop'):
return qs.order_by(...)
return qs

Related

How To Join Two Models with Different Column Names and Return All Instances?

I aim to create a dataframe of the Top 3 Selling menu_items in my Purchases table. My thoughts are to create a join on the Purchases model with the Menu_Item model where Purchases.menu_item = Menu_Item.title. I will convert the QuerySet to a DataFrame using django_pandas.io. I plan to use the sum of Menu_Item.price associated with each distinct Purchases.menu_item to determine the Top 3 menu_items of all the records in the Purchases table.
My problem is that I cannot join the two tables successfully. I’ve scoured the interwebz for a working solution to join two models with different field names, which returns all instances, and I tried various solutions, but the scarce articles on this topic yielded no joy.
models.py
...
class MenuItem(models.Model):
title = models.CharField(max_length=100, unique=True,
verbose_name="Item Name")
price = models.FloatField(default=0.00, verbose_name="Price")
description = models.CharField(max_length=500,
verbose_name="Item Description")
def __str__(self):
return f"title={self.title}; price={self.price}"
def get_absolute_url(self):
return "/menu"
def available(self):
return all(X.enough() for X in self.reciperequirement_set.all())
class Meta:
ordering = ["title"]
class Purchase(models.Model):
menu_item = models.ForeignKey(MenuItem, on_delete=models.CASCADE,
verbose_name="Menu Item")
timestamp = models.DateTimeField(auto_now_add=True,
verbose_name="DateTime")
def __str__(self):
return f"menu_item=[{self.menu_item.__str__()}]; time={self.timestamp}"
def get_absolute_url(self):
return "/purchases"
class Meta:
ordering = ["menu_item"]
I tried adapting too many unsuccessful code fragments to reproduce here, so I am looking at starting with a clean slate. I'm hoping you have an effective solution to share. Your help is greatly appreciated. Thanks.
You didn't mention what you have tried, so it is hard for me (and other developers) to give precise suggestions.
Anyway, have you tried something like
purchases = Purchase.objects.values(
'timestamp',
item_title=F('menu_item__title'),
item_price=F('menu_item__price'),
item_desc=F('menu_item__description'))
This queryset will fetch all values in one sql connection.

Django 4.0. Updating field once every day

I have a Book model that has a integer field count which is the number of users that have this book in their readlist. Users can add a book to their ReadList model (many to many field). I want to update the count in the book model once a day...how should I go about doing this?
Will be using this to displaying trending books and book rank based on user count.
Book Model:
class Book(models.Model):
name = models.CharField(max_length=150, unique=True)
description = models.TextField()
user_count = models.IntegerField()
pages = models.IntegerField()
genres = models.ManyToManyField(Genre)
def __str__(self):
return self.name
ReadList Model:
class ReadList(models.Model):
user = models.OneToOneField(User, on_delete=models.CASCADE)
readlist = models.ManyToManyField(Book, related_name='readlist', blank=True)
def __str__(self):
return self.user.username
Django unfortunately doesn't do scheduling very well. You can actually generate this information already, via the related_name, with the advantage this will be realtime!
Let's start with a queryset for all books. Imagine you put this in one of your views.
books = Book.objects.all()
Now you have all your books, but no data on booklist numbers. So we can annotate that information via the query
from django.db.models import Count
books = Book.objects.all().annotate(user_count = Count('readlist')
Useful, but in no particular order, lets arrange it from highest to lowest.
books = Book.objects.all().annotate(user_count = Count('readlist') .order_by('-user_count')
But do we want it for every book in the store? Let's limit to the top 10.
books = Book.objects.all().annotate(user_count = Count('readlist') .order_by('-user_count')[:10]
The books don't necessarily know their own place in the heirarchy, though. Lets cycle through them and give them another impromptu field (Querysets aren't really indexed, so we can do this ourselves)
for index, book in enumerate(books):
book.placing = index
Pass context['books'] = books to your template and you should be able to do the following:
{% for book in books %}
Title:{{book.title}} <br>
Position:{{book.placing}} <br>
In :{{book.user_lists}} reading lists<br>
And there you have it, an up to the minute list of the top 10 books sorted by user_count without having to use external scheduling. Make sure you add the palcing last, as any further DB queries on the queryset will cause the query to be remade.

Loop through and Merge Querysets

I am sure this one is straight forward but I cannot seem to get my head around it.
I have "Users" who can post "Posts" on my site.
Each user can follow other users.
The idea is to display all the posts posted by the users that current user is following.
Example : Foo followed Bar and Baz. I need to retrieve all the posts from Bar and Baz.
Bar = Post.objects.filter(user=3)
Baz = Post.objects.filter(user=4)
totalpost= list(chain(Bar, Baz))
print(totalpost)
On this occasion, when both variables userXposts and temp are hardcoded, I can easily retrieve ONE list of QuerySets neeatly by chaining both QuerySets.
However, I cannot have those hardcoded. As such, I am attempted to loop through each user posts and add it in a list since my user can follow X amount of users :
QuerySet = Profile.objects.filter(follower=1)
for x in QuerySet:
userXposts = Post.objects.filter(user=x.user.id)
temp = userXposts
totalpost= list(chain(userXposts, temp))
temp = []
print("Totalpost after union of userpost and temp: ", totalpost)
Here, Profile.objects.filter(follower=1) return two sets of QuerySets, one for Baz and one for Bar.
The problem that I have so far is that totalpost endup being a "list of list" (I believe) which forces me to call totalpost[0] for Bar posts and totalpost[1] for Baz posts.
Since I am attempting to use Pagination with Django, I am forced to pass ONE Variable only in p= Paginator(totalpost, 200)
Would you be able to assist in the loop so that I can fetch the data for the first user, add it to a variable, then go to the second user and ADD the second QuerySet data to the list where the First User data is?
Thanks a lot !
EDIT :
Here are the Models :
class User(AbstractUser):
pass
class Profile(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
following = models.ManyToManyField(User, blank=True, related_name="following_name")
follower = models.ManyToManyField(User, blank=True, related_name="follower_name")
def __str__(self):
return f'"{self.user.username}" is followed by {self.follower.all()} and follows {self.following.all()}'
class Post(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
timestamp = models.DateTimeField(default=datetime.now)
post = models.CharField(max_length=350, null=True, blank=True)
like = models.ManyToManyField(User, blank=True, related_name="like_amount")
def __str__(self):
return f'#{self.id}: "{self.user.username}" posted "{self.post}" on "{self.timestamp}". Like : "{self.like.all()}" '
Post.objects.filter(user__ following_name__id=1)

How to reduce quantity of an item in main table when it is being used in another table - django

I am creating my model in Django and I have a many to many relationship between supplies and van kits. The idea is that an "item" can belong to many "van kits" and a "van kit" can have many " items. I created an intermediary model that will hold the relationship, but I am struggling to figure out a way to relate the quantity in the van kit table to the quantity in the main supplies table. For example, if I wanted to mark an item in the van kit as damaged and reduce the quantity of that supply in the van kit, I would also want to reduce the total count of that supply in the main "supplies" table until it has been replenished. I am thinking that maybe I'll have to create a function in my views file to carry out that logic, but I wanted to know if it could be implemented in my model design instead to minimize chances of error. Here's my code:
class supplies(models.Model):
class Meta:
verbose_name_plural = "supplies"
# limit the user to selecting a pre-set category
choices = (
('CREW-GEAR','CREW-GEAR'),
('CONSUMABLE','CONSUMABLE'),
('BACK-COUNTRY','BACK-COUNTRY')
)
supplyName = models.CharField(max_length=30, blank=False) # if they go over the max length, we'll get a 500 error
category = models.CharField(max_length=20, choices = choices, blank=False)
quantity = models.PositiveSmallIntegerField(blank=False) # set up default
price = models.DecimalField(max_digits=5, decimal_places=2, null=True, blank=True) # inputting price is optional
def __str__(self):
return self.supplyName
class van_kit(models.Model):
supply_name = models.ManyToManyField(supplies, through='KitSupplies',through_fields=('vanKit','supplyName'), related_name="supplies")
van_kit_name = models.CharField(max_length=100)
vanName = models.ForeignKey(vans, on_delete=models.CASCADE)
def __str__(self):
return self.van_kit_name
class KitSupplies(models.Model):
supplyName = models.ForeignKey(supplies, on_delete=models.CASCADE)
vanKit = models.ForeignKey(van_kit, on_delete=models.CASCADE)
quantity = models.PositiveSmallIntegerField(blank=False)
def __str__(self):
return str(self.supplyName)
class Meta:
verbose_name_plural = 'Kit Supplies'
I am fairly new to django, I have to learn it for a class project so if my logic is flawed or if a better way to do it is obvious, please respectfully let me know. I'm open to new ways of doing it. Also, I've read through the documentation on using "through" and "through_fields" to work with the junction table, but I'm worried I may not be using it correctly. Thanks in advance.
One option would be to drop/remove the field quantity from your supplies model and just use a query to get the total quantity.
This would be a bit more expensive, as the query would need to be run each time you want to know the number, but on the other hand it simplifies your design as you don't need any update logic for the field supplies.quantity.
The query could look as simple as this:
>>> from django.db.models import Sum
>>> supplies_instance.kitsupplies_set.aggregate(Sum('quantity'))
{'quantity__sum': 1234}
You could even make it a property on the model for easy access:
class supplies(models.Model):
...
#property
def quantity(self):
data = self.kitsupplies_set.aggregate(Sum('quantity'))
return data['quantity__sum']

A QuerySet by aggregate field value

Let's say I have the following model:
class Contest:
title = models.CharField( max_length = 200 )
description = models.TextField()
class Image:
title = models.CharField( max_length = 200 )
description = models.TextField()
contest = models.ForeignKey( Contest )
user = models.ForeignKey( User )
def score( self ):
return self.vote_set.all().aggregate( models.Sum( 'value' ) )[ 'value__sum' ]
class Vote:
value = models.SmallIntegerField()
user = models.ForeignKey( User )
image = models.ForeignKey( Image )
The users of a site can contribute their images to several contests. Then other users can vote them up or down.
Everything works fine, but now I want to display a page on which users can see all contributions to a certain contest. The images shall be ordered by their score.
Therefore I have tried the following:
Contest.objects.get( pk = id ).image_set.order_by( 'score' )
As I feared it doesn't work since 'score' is no database field that could be used in queries.
Oh, of course I forget about new aggregation support in Django and its annotate functionality.
So query may look like this:
Contest.objects.get(pk=id).image_set.annotate(score=Sum('vote__value')).order_by( 'score' )
You can write your own sort in Python very simply.
def getScore( anObject ):
return anObject.score()
objects= list(Contest.objects.get( pk = id ).image_set)
objects.sort( key=getScore )
This works nicely because we sorted the list, which we're going to provide to the template.
The db-level order_by cannot sort queryset by model's python method.
The solution is to introduce score field to Image model and recalculate it on every Vote update. Some sort of denormalization. When you will can to sort by it.

Categories

Resources