I have a model containing various entries tied to one user and I want to give the user a view where he can review these entries, select some of them and perform an action on the selection. something like the admin intereface has. I have tried UpdateView but that is for one entry only. ListView doesn't like that the model returns multiple entries for one identificator. Is there something else I could use?
EDIT:
Below is the model, I am talking about. A user will have multiple model entries and I just want a view that lists these multiple entries and allows the user to perform a bulk action on them, like delete ...
class UserData(models.Model):
class Meta:
app_label = "app"
user_id = models.IntegerField()
name = models.CharField(_("Name"),max_length=100)
latdeg = models.IntegerField(_('Latitude'))
latmin= models.IntegerField(_('Latitude'), validators=[validate_60])
londeg = models.IntegerField(_('Longitude'))
lonmin= models.IntegerField(_('Longitude'), validators=[validate_60])
main = models.BooleanField()
def __unicode__(self):
return user_id + "-" + self.name
I think what you are looking for is inlineformset_factory
Since you have not given any example, I suggest you look at the example of One author, multiple books as given in this SO post.
Related
I have a Users table which is FK to a table called Post. How can I get only the last Post that the user registered? The intention is to return a list of users with the last registered post, but when obtaining the users, if the user has 3 posts, the user is repeated 3 times. I'm interested in only having the user once. Is there an alternative that is not unique?
class User(models.Model):
name = models.CharField(max_length=50)
class Post(models.Model):
title = models.CharField(max_length=50)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts', related_query_name='posts')
created = models.DateTimeField(default=timezone.now)
class Meta:
get_latest_by = 'created'
ordering = ['-created']`
I already tried with selected_related and prefetch_related, I keep getting multiple user registrations when they have multiple Posts.
user = User.objects.select_related('posts').all().values_list('id', 'name', 'posts__title', 'posts__created')
This does give me the answer I want, but when I change the created field to sort by date, I don't get the newest record, I always get the oldest.
user = User.objects.select_related('posts').all().values_list('id', 'name', 'posts__title', 'posts__created').distinct('id')
I'm trying to do it without resorting to doing a record-by-record for and getting the most recent Post. I know that this is an alternative but I'm trying to find a way to do it directly with the Django ORM, since there are thousands of records and a for is less than optimal.
In that case your Django ORM query would first filter posts by user then order by created in descending order and get the first element of the queryset.
last_user_post = Post.objects.filter(user__id=1).order_by('-created').first()
Alternatively, you can use an user instance:
user = User.objects.get(id=1)
last_user_post = Post.objects.filter(user=user).order_by('-created').first()
I have a django project and I have a Post model witch look like that:
class BasicPost(models.Model):
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
published = models.BooleanField(default=False)
created_date = models.DateTimeField(auto_now_add=True)
title = models.CharField(max_length=100, blank=False)
body = models.TextField(max_length=999)
media = models.ImageField(blank=True)
def get_absolute_url(self):
return reverse('basic_post', args=[str(self.pk)])
def __str__(self):
return self.title
Also, I use the basic User model that comes with the basic django app.
I want to save witch posts each user has read so I can send him posts he haven't read.
My question is what is the best way to do so, If I use Many to Many field, should I put it on the User model and save all the posts he read or should I do it in the other direction, put the Many to Many field in the Post model and save for each post witch user read it?
it's going to be more that 1 million + posts in the Post model and about 50,000 users and I want to do the best filters to return unread posts to the user
If I should use the first option, how do I expand the User model?
thanks!
On your first question (which way to go): I believe that ManyToMany by default creates indices in the DB for both foreign keys. Therefore, wherever you put the relation, in User or in BasicPost, you'll have the direct and reverse relationships working through an index. Django will create for you a pivot table with three columns like: (id, user_id, basic_post_id). Every access to this table will index through user_id or basic_post_id and check that there's a unique couple (user_id, basic_post_id), if any. So it's more within your application that you'll decide whether you filter from a 1 million set or from a 50k posts.
On your second question (how to overload User), it's generally recommended to subclass User from the very beginning. If that's too late and your project is too far advanced for that, you can do this in your models.py:
class BasicPost(models.Model):
# your code
readers = models.ManyToManyField(to='User', related_name="posts_already_read")
# "manually" add method to User class
def _unread_posts(user):
return BasicPost.objects.exclude(readers__in=user)
User.unread_posts = _unread_posts
Haven't run this code though! Hope this helps.
Could you have a separate ReadPost model instead of a potentially large m2m, which you could save when a user reads a post? That way you can just query the ReadPost models to get the data, instead of storing it all in the blog post.
Maybe something like this:
from django.utils import timezone
class UserReadPost(models.Model):
user = models.ForeignKey("auth.User", on_delete=models.CASCADE, related_name="read_posts")
seen_at = models.DateTimeField(default=timezone.now)
post = models.ForeignKey(BasicPost, on_delete=models.CASCADE, related_name="read_by_users")
You could add a unique_together constraint to make sure that only one UserReadPost object is created for each user and post (to make sure you don't count any twice), and use get_or_create() when creating new records.
Then finding the posts a user has read is:
posts = UserReadPost.objects.filter(user=current_user).values_list("post", flat=True)
This could also be extended relatively easily. For example, if your BasicPost objects can be edited, you could add an updated_at field to the post. Then you could compare the seen_at of the UserReadPost field to the updated_at field of the BasicPost to check if they've seen the updated version.
Downside is you'd be creating a lot of rows in the DB for this table.
If you place your posts in chronological order (by created_at, for example), your option could be to extend user model with latest_read_post_id field.
This case:
class BasicPost(models.Model):
# your code
def is_read_by(self, user):
return self.id < user.latest_read_post_id
I'm making an inventory app to control the existence of tools in my workshop. Besides knowing how many things I have, I want to know where things are (what tool cart the tool is in ) and who owns the tool cart (Employee). I also need to keep a record of all damaged tools. I've been going about this in the following way:
1.- I have a model called Item that has all common filed for all tools, then I create a new model per tool type with specific field for each tool type i.e.(end-mill-cutters, drill-bits, screws, etc ). these tool Type models all inherit from Item as Multi-table inheritance.
2.- I made the models for my tools carts and its called Carritos( in spanish) this table has a One To One relation ship to Employees( since a carrito can be owned by one person only). It also has a Many To Many relationship to my Item table trough a secondary model called Transaccion, this model handles make the relation between Carrito and Items
this is the Carritos model
class Carritos(models.Model):
no_carrito = models.CharField(max_length=3, unique=True)
empleado = models.OneToOneField(Empleados, on_delete=models.CASCADE)
# empleado = models.ManyToManyField(Empleados, through='Transaccion')
items = models.ManyToManyField(Item, through='Transaccion', related_name='carritos')
f_creacion = models.DateTimeField(auto_now_add=True)
f_actualizacion = models.DateTimeField(auto_now=True)
activo = models.BooleanField(default=True)
def get_absolute_url(self):
return reverse('inventario:carrito')#, kwargs={'pk': self.pk})
class Meta:
verbose_name_plural = "Carritos"
def __str__(self):
return self.no_carrito
class Transaccion(models.Model):
carrito = models.ForeignKey(Carritos, on_delete=models.CASCADE, related_name='items_carrito')
herramienta = models.ForeignKey(Item, on_delete=models.CASCADE, related_name='items_carrito')
cantidad = models.PositiveSmallIntegerField(default=1)
activo = models.BooleanField(default=True)
tipo = models.CharField(max_length=10, choices=CONSUMIBLE, blank=True, null=True)
motivo = models.CharField(max_length=10, blank=True, null=True)
def get_absolute_url(self):
return reverse('inventario:carrito')#, kwargs={'pk': self.pk})
3.- The idea I had to get the carritos logic is to get a list of existing carts in the carts main page and have a button bind to a CreateView CBV to create a new carrito if needed.
This list would also have a button bind to an UpdateView CBV in order to be able to change the employee in case the employee quits and an other button bind to a function that in theory would work as a DetailView to see all data assigned to carrito like (employee assigned to it, carrito number, and all Items in the carrito).
My intention was to be able to add an Item inside this view and have all items listed, I managed to be able to add Items and also managed to display all Items and the amount of those Items the carrito has. I had some issues on how to go about when multiple items of the same kind needed to be added to the carrito (let's say I needed to add 2 cutters exactly the same). But I figured that since I all ready had the Transaccion table, and this table tied Item to Carritos. I could use this to record every items as 1 of each and have an active field as Boolean, this way I could display and aggregate all distinct items and sum totals of every item in my view. It works for displaying the quantities.
The problem I'm currently are having, is if I want to edit a tool type and deactivate one of the items in the transaction model I always get the firs items on the list no matter how I choose to filter it.
My views for carritos creation
# =========================================================================== #
# LOGICA PARA CREAR CARRITOS
# =========================================================================== #
# ===================> Logica relacinado con Cortadores <=====================#
def home_carrito(request):
template_name = 'inventario/carrito/createcarrito.html'
model = Carritos
carritos = Carritos.objects.all()
if carritos:
return render(request, template_name, {'carritos':carritos})
else:
return render(request,template_name)
class CarritoCreate(CreateView):
model = Carritos
fields = [
'no_carrito',
'empleado',
'activo',
]
class ItemCreate(CreateView):
model = Transaccion
fields = [
'carrito',
'herramienta',
]
def detalle_carrito(request, pk):
model = Carritos, Transaccion
template_name = 'inventario/carrito/detalles_carrito.html'
carritos = Carritos.objects.filter(pk=pk)
# GEST ALL TOOLS ASSIGNE TO CARRITO'S PK THAT ARE ACTIVE
# TRY TO GET ALL ACTIVE ITEMS THAT BELONG TO CARRITO = PK AND AGREGATE TOTAL ITEMS PER TYPE
cantidades = Transaccion.objects.values('herramienta__description').annotate(Sum('cantidad')).filter(activo=True, carrito_id=pk)
# GEST ALL TOOLS ASSIGNE TO CARRITO'S PK THAT ARE NOT ACTIVE
eliminados = Transaccion.objects.filter(activo=False,carrito_id=pk)
return render(request,template_name, {'carrito':carritos, 'trans':cantidades, 'eliminados':eliminados})
class CarritoUpdate(UpdateView):
model = Carritos
fields = [
'no_carrito',
'empleado',
'activo',
]
template_name_suffix = '_update_form'
def ItemUpdate(UpdateView):
model = Transaccion
fields = [
'carrito',
'herramienta',
'cantidad',
'tipo',
'motivo',
'activo',
]
template_name_suffix = '_update_form'
def detalle_Items(request, pk):
model = Transaccion
template_name = 'inventario/carrito/test-template.html'
try:
items_update = Transaccion.objects.filter(activo=True, carrito_id=pk, herramienta_id=pk)
except Transaccion.DoesNotExist:
raise Http404()
return render(request, template_name, {'items_update':items_update})
So what I need in the first place is to know if what I'm doing is logical? or make sense. Scond thing I need is to know if there a better way and how?
and finally I need help resolving my issue: I need to get into an updateview for every Item in my Transaccion model and be able to disable or enable that record.
Different people would take different approaches. So far I think what you have built would absolutely work, but what becomes complicated is building the realtime inventories, which looks like its built from the transaction log. What I see as missing from the model is the snapshot of real time inventories available. Because we aren't talking about a data model that changes so frequently - like ad impressions - you can store that upon transaction vice computing it as needed.
For instance, your global inventory of hammers is 5 hammers. One employee adds a hammer to a cart. From here you articulated a couple of different use cases. One is that you need to know that employee XYZ (which implies a specific cart based on the 1-to-1) has that specific hammer. What you'd also like to know is how many hammers you have available? You may also want to understand the turnover of specific assets. Does employee XYZ maintain items in his cart longer than the average employee?
To do this I think you'd need to talk about the API layer which orchestrates that logic and the addition of another object which snapshots actual inventory instead of computing that from the transaction log. Why I bring up the API layer is that it may be a cleaner abstraction to place the logic for orchestrating multiple model changes in that than having the model itself house that logic.
So in short, I think what you've built works - but the logical expression of the use cases you've articulated are handled at the viewset/ modelviewset layer in an API. Because thats where you'll need to prep the data to be loaded into a specific format for visualization. And thats where what is easily serializable becomes the dominant force in model complexity.
im trying to optimize dbase queries from the admin, by caching the results and using in coming queries. But - i still see dbase request for each entry.
i have a model like this:
class actions(models.Model):
user = models.ForeignKey(MyUser)
action = ...
class Myuser(Models.Model):
name = models.CharField()
company = models.ForeignKey(Companies)
in the admin change_list, i'd like to see a table with:
user action company_name
so i defined my admin as follows:
class ActionsAdmin(admin.ModelAdmin):
list_display = ('user','action','company_name')
...
def company_name(self,instance):
return instance.user.company
When i run this, i see that for each user, there is a query to the company model to extract the company's name. However, since there are not many companies and in many cases, users perform many actions once after the other, i want to query for all companies once and then use the cached result instead of accessing the dbase for each entry.
How can i do that?
Use list_select_related to specify the relation to select with the query, so in your case:
class ActionsAdmin(admin.ModelAdmin):
list_display = ('user','action','company_name')
list_select_related = ('user__company', )
...
def company_name(self,instance):
return instance.user.company
Edit:
You can also specify the field directly on list_display, without the need for custom method.
It should handle list_select_related for you (at least according to the docs linked above). If assuming your Companies model has a name field:
class ActionsAdmin(admin.ModelAdmin):
list_display = ('user','action','user__company__name')
I'm stuck trying to figure how to do the following:
I have a few entities:
PurchaseItem (an item in user's cart),
Order (an order - combines one or many PurchaseItems),
OrderStatusHistory (that's status items for the Order - instead of changing, I create new ones to be able to retrospectively preview how status changed over time).
I don't want any of these to be created via admin - they are all created via public interface, but I have to show the Order and its attributes in the admin panel:
I need to be able to show list of orders. That's simple.
When I click on an order or something I want to be able to view the order's details:
list of Purchase items.
I need to be able to change the status of the order - selecting from a drop down or something - however, this action show be triggering a new statusHistory item creation.
Is this all possible with admin interface or should I forget about it and create my own implementation with pages and all?
My models look like this:
class Order(models.Model):
dateCreated = models.DateTimeField(null=False,default=datetime.now())
items = models.ManyToManyField(PurchaseItem)
user_name = models.CharField(null=True,blank=True,max_length=200)
phone = models.CharField(null=False,blank=False,max_length=11,validators=[validate_phone])
phone_ext = models.CharField(null=True,blank=True,max_length=5,validators=[validate_phone_ext])
email = models.CharField(null=False,blank=False,max_length=100,validators=[validators.EmailValidator])
addressCity = models.CharField(null=False,blank=False,max_length=100)
addressStreet = models.CharField(null=False,blank=False,max_length=200)
notes = models.TextField(null=True,blank=True)
accessKey = models.CharField(max_length=32,default=CreateAccessKey())
class PurchaseItem(models.Model):
picture = models.ForeignKey(Picture, null=False)
paperType = models.CharField(null=False,max_length=200)
printSize = models.CharField(null=False,max_length=200)
quantity = models.IntegerField(default=1, validators=[validators.MinValueValidator(1)])
price = models.DecimalField(decimal_places=2,max_digits=8)
dateCreated = models.DateTimeField(null=False)
cost = models.DecimalField(decimal_places=2,max_digits=8)
class OrderStatusHistory(models.Model):
orderId = models.ForeignKey(Order)
dateSet = models.DateTimeField(null=False,default=datetime.now())
status = models.IntegerField(choices=OrderStatus,default=0,null=False,blank=False)
comment = models.TextField(null=True,blank=True)
The following inline setup doesn't work because Order doesn't have a FK to PurchaseItems:
class OrderStatusHistoryAdmin(admin.StackedInline):
model = OrderStatusHistory
class PurchaseItemAdmin(admin.StackedInline):
model = PurchaseItem
class OrderAdmin(admin.ModelAdmin):
model = Order
inlines = [OrderStatusHistoryAdmin,PurchaseItemAdmin]
admin.site.register(Order,OrderAdmin)
Part 1
Use Inlines, that's very straight forward and django excels at this.
Part 2
Sure you could override your save for example and check if the drop down item has changed. If it has, generate your order status history object.
def save(self, *args, **kwargs):
if self._initial_data['status'] != self.__dict__['status']:
self.orderstatushistory_set.create("Status Changed!")
super(Order, self).save(*args, **kwargs)
You could do the same thing in the ModelAdmin too
def save_model(self, request, obj, form, change):
if obj._initial_data['status'] != obj.__dict__['status']:
# create whatever objects you wish!
Part 1:
You can 'nest' models with TabularInline or StackedInline admin models.
class OrderAdmin(admin.ModelAdmin):
model = Order
inlines = [
OrderStatusAdmin,
PurchaseItemAdmin
]
class OrderStatusAdmin(admin.StackedInline):
model = OrderStatus
class PurchaseAdmin(admin.StackedInline):
model = PurchaseItem
More information can be found here: http://docs.djangoproject.com/en/dev/ref/contrib/admin/#inlinemodeladmin-objects
Part 2:
I need to be able to change the status of the order - selecting from a drop down or something - however, this action show be triggering a new statusHistory item creation.
For this you can use signals. There is a post_save and pre_save. So each time you save an order you can add extra logic. The pre_save signal has a sender and an instance so I think you can compare the status of the sender and the instance to be saved and if it changed you can add an other OrderStatus model.
More info can be found here:
http://docs.djangoproject.com/en/dev/ref/signals/#pre-save