Django inventory app tool cart update view - django

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.

Related

Django Model queries with relationships. How to do the right join

Let's say I have 2 Models:
class Auction(models.Model):
seller = models.ForeignKey(User, on_delete=models.CASCADE, related_name="seller")
title = models.CharField(max_length=64)
class Watchlist(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_watchlist')
auction = models.ForeignKey(Auction, on_delete=models.CASCADE, related_name='auction_watchlist')
The view receives a request, creates a context variable with the auction objects the are:
associated with the user who made the request and
that have been added to the Watchlist Model,
sends it to the template.
I have set up my view to work like this:
#login_required
def watchlist(request):
watchlist_objects = Watchlist.objects.filter(user=request.user)
auction_objects = Auction.objects.filter(auction_watchlist__in=watchlist_objects).all()
context = {'watchlist_auctions': auction_objects}
print(context)
return render(request, "auctions/watchlist.html", context)
-I make the first query to get the list of items in the watchlist associate with the user.
-Then I use that to get another query from the Auction Model and I pass it to the template.
In the template I can access the attributes of Auction to display them. (title, author, and others that I did not include for simplicity)
The question is:
Is this the "right way? Is there a better way to access the attributes in Auction from the first Watchlist query?
It seems to me that I'm doing something overcomplicated.
This is not that bad, considering that it will probably be executed as one query, because of the lazy queryset evaluations. You can skip the .all() if you already have .filter().
However, there is a more convenient way to do this, using lookups that span relationships.:
auction_objects = Auction.objects.filter(auction_watchlist__user_id=request.user.id)

Multiple sub-fields attached to a field, and how to give the user the possibility of adding many of these fields

I am building a form in which users (composers) can add a composition. Within this form, alongside title, year, etc, they also add instrumentation.
Each instrument can have a couple of properties, for example 'doubling', and the number of players.
So, for instance:
title: New composition
instrumentation:
violin
doubled: no
players: 1 (this is the 'DoublingAmount', see the models)
viola
doubled: yes
players: 2 (this is the 'DoublingAmount', see the models)
cello
doubled: no
players: 4 (this is the 'DoublingAmount', see the models)
I have created three different models: one for instrumentation, one for the composition, and then one with a ManyToMany relation via a 'through'.
models.py:
class Composition(models.Model):
title = models.CharField(max_length=120) # max_length = required
INSTRUMENT_CHOICES = [('pno', 'piano'), ('vio1', 'violin_1(orchestral section)'), ('vio2', 'violin_2(orchestral section)'),]
instrumentation = models.ManyToManyField('Instrumentation',
through='DoublingAmount',
related_name='compositions',
max_length=10,
choices=INSTRUMENT_CHOICES,)
class Instrumentation(models.Model):
instrumentation = models.CharField(max_length=10)
class DoublingAmount(models.Model):
DOUBLING_CHOICES =[(1, '1'),(2, '2'), (3, '3')]
doubling = models.BooleanField(default=False)
composition = models.ForeignKey(Composition, related_name='doubling_amount', on_delete=models.SET_NULL, null=True)
instrumentation = models.ForeignKey(Instrumentation, related_name='doubling_amount', on_delete=models.SET_NULL, null=True)
amount = models.IntegerField(choices=DOUBLING_CHOICES, default=1)
forms.py:
from django import forms
from .models import Composition, Instrumentation
class CompositionForm(forms.ModelForm):
title = forms.CharField(label='Title',
widget=forms.TextInput()
description = forms.CharField()
class Meta:
model = Composition
fields = [
'title',
'instrumentation',
]
views.py:
def composition_create_view(request):
form = CompositionForm(request.POST or None)
if form.is_valid():
form.save()
form = CompositionForm()
context = {
'form': form
}
return render(request, "template.html", context)
template.html:
{{ form }}
I can see the drop-down list in my form giving the choice of the name of the instrument, and only that. I'd like to have the possibility of selecting name of instrument, doubling yes/no, and quantity. Then I also want to add more instruments, each with its own name, doubling, and quantity. I know this will have to be done via JavaScript, but I don't know how to build the 'behind the scenes' and get Django grab the new items added by the user.
Update
I've added a new form in forms.py:
class InstrumentationForm(forms.ModelForm):
class Meta:
model = DoublingAmount
fields = [
'doubling',
'amount',
'instrumentation'
]
and linked to the views.py:
form_d = InstrumentationForm(request.POST or None)
[...]
context = {
'form': form,
'form_d': form_d
}
and, finally, the template:
{{ form_d }}
I now have the instrumentation, doubling, and amount fields showing in my template. Am I doing this right? If yes, how can I have Django deal with the user wanting to add or remove instruments? Thanks.
Update 2
The issue, which I just realised, is that if I use the DoublingAmount model to store information about the composition's instrument, the object Composition needs to be already created, which of course it's not may case. One solution maybe is to build another form with only 'DoublingAmount' (e.g. instrumentation) information to be displayed after the user has saved the title of the composition. Still, I don't know how to have more than one instrument within the DoublingAmount model without having several objects of DoublingAmount objects for the same composition.
Is there a simpler way to do all this? For example, a 'sub-class' of the composition model?
Create a Composition and then add one Instrument after another like comments to a blog-post. That would probably be the easiest way. Sadly I dont know how to create an array of objects from a form in Django. It's the right way to have more than one object than, thats why its ManyToMany. I would also suggest OneToMay and just two classes:
Composition and instrument with the fields: name, doubled, player-count. But I also dont really get this musician stuff so maybe I am wrong with that :-)
Another way would be using Ajax or stuff like that and create an array in json and transforming that into your instruments. You would need js anyway if you want to dynamically add more instruments (you would need to create input fields and so on).

Django change multiple model entries

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.

Listing all related objects and allow paging on related objects

Can someone give me the best approach with an example for the following...
On a page I load the 'Group' object by ID. I also want to list all contacts that belong to that group (with paging).
Because of the paging issue I was thinking of just running a second database query with...
In my view...
group = get_object_or_404(Group, pk=id)
contacts = Contacts.objects.filter(group=x)
But this seems wasteful as I'm already getting the Group why hit the database twice.
See my model.
class GroupManager(models.Manager):
def for_user(self, user):
return self.get_query_set().filter(user=user,)
class Group(models.Model):
name = models.CharField(max_length=60)
modified = models.DateTimeField(null=True, auto_now=True,)
#FK
user = models.ForeignKey(User, related_name="user")
objects = GroupManager()
def get_absolute_url(self):
return reverse('contacts.views.group', args=[str(self.id)])
class Contact(models.Model):
first_name = models.CharField(max_length=60)
last_name = models.CharField(max_length=60)
#FK
group = models.ForeignKey(Group)
This is what select_related is designed for:
Returns a QuerySet that will automatically “follow” foreign-key
relationships, selecting that additional related-object data when it
executes its query. This is a performance booster which results in
(sometimes much) larger queries but means later use of foreign-key
relationships won’t require database queries.
In your case it would be:
Group.objects.select_related().get(pk=group)
Now on each FK lookup, you won't hit the database again.
The next step would be to cache the results using the cache api so that you don't hit the database everytime the next "page" is called. This would be useful if your data isn't time sensitive.

Show complex entity in Django Admin interface

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