Django - update database item if exists or insert if not exists - python

I want to update item quantity in a database or add to inventory if the UPC doesn't already exist. I am not sure how to write the logic. Here's what I have so far:
My view:
from django.contrib.auth.decorators import login_required
from .forms import AuditItemForm
from .models import AuditItem
import datetime
#login_required
def audit_inventory(request):
form = AuditItemForm(request.POST or None)
if form.is_valid():
form.save(commit=False)
form_upc = form.cleaned_data.get('upc')
qty = form.cleaned_data.get('qty')
for instance in AuditItem.objects.all():
if instance.upc == form_upc:
instance.qty += qty
instance.user = str(request.user)
instance.last_updated = datetime.datetime.now()
instance.save()
elif instance.upc != form_upc:
form.save(commit=True)
return redirect('/audit')
context = {
"form": form,
"title": "Audit Inventory",
}
return render(request, "audit.html", context)
What's wrong with my logic? It updates an item correctly but it doesn't allow me to add a new item that doesn't already exist.

Not sure if it is what you want but maybe try get_or_create() function it will return item and boolean value if it was created or pulled from database
form = AuditItemForm(request.POST or None)
if form.is_valid():
form.save(commit=False)
form_upc = form.cleaned_data.get('upc')
qty = form.cleaned_data.get('qty')
# get item or create new one if it doesn't exist
item, created = AuditItem.objects.get_or_create(upc=form_upc)
# if it already exist, update quantity
if not created:
item.qty += qty
item.user = str(request.user)
item.last_updated = datetime.datetime.now()
# whether item was created or updated save it to database
item.save()

What you need is update_or_create(defaults=None, ** kwargs)
Explanation:
A convenience method for updating an object with the given kwargs, creating a new one if necessary. The defaults is a dictionary of (field, value) pairs used to update the object. The values in defaults can be callables.
Returns a tuple of (object, created), where object is the created or updated object and created is a boolean specifying whether a new object was created.
The update_or_create method tries to fetch an object from database based on the given kwargs. If a match is found, it updates the fields passed in the defaults dictionary.
The example of this method is:
obj, created = Person.objects.update_or_create(
first_name='John', last_name='Lennon',
defaults={'first_name': 'Bob'},
)
For more information: https://docs.djangoproject.com/en/3.2/ref/models/querysets/#update-or-create

Related

Value does not change in admin dashboard and html template, view function shows the correct one

Value does not change in admin dahboard and html template that has a tag of the value, inrthe view function where the change happens,it print the correct value that was changed (order.status)
def chef_order(request):
chef = request.user.vendor
orders = chef.orders.all()
if 'btnform1' in request.POST:
orderid = request.POST.get("orderid")
order = Order.objects.get(pk=int(orderid))
sts = 'confirmed'
order.status = "confirmed"
print(order.get_status_display())
order = Order.objects.get(pk=int(orderid))
This returns an instance object of the database record. However, it's not the record itself. When you update the field on the instance, you need to save it back to the database.
order.status = "confirmed"
order.save()

Django: Pass a variable/parameter to form from view? [duplicate]

I have a Model as follows:
class TankJournal(models.Model):
user = models.ForeignKey(User)
tank = models.ForeignKey(TankProfile)
ts = models.IntegerField(max_length=15)
title = models.CharField(max_length=50)
body = models.TextField()
I also have a model form for the above model as follows:
class JournalForm(ModelForm):
tank = forms.IntegerField(widget=forms.HiddenInput())
class Meta:
model = TankJournal
exclude = ('user','ts')
I want to know how to set the default value for that tank hidden field. Here is my function to show/save the form so far:
def addJournal(request, id=0):
if not request.user.is_authenticated():
return HttpResponseRedirect('/')
# checking if they own the tank
from django.contrib.auth.models import User
user = User.objects.get(pk=request.session['id'])
if request.method == 'POST':
form = JournalForm(request.POST)
if form.is_valid():
obj = form.save(commit=False)
# setting the user and ts
from time import time
obj.ts = int(time())
obj.user = user
obj.tank = TankProfile.objects.get(pk=form.cleaned_data['tank_id'])
# saving the test
obj.save()
else:
form = JournalForm()
try:
tank = TankProfile.objects.get(user=user, id=id)
except TankProfile.DoesNotExist:
return HttpResponseRedirect('/error/')
You can use Form.initial, which is explained here.
You have two options either populate the value when calling form constructor:
form = JournalForm(initial={'tank': 123})
or set the value in the form definition:
tank = forms.IntegerField(widget=forms.HiddenInput(), initial=123)
Other solution: Set initial after creating the form:
form.fields['tank'].initial = 123
If you are creating modelform from POST values initial can be assigned this way:
form = SomeModelForm(request.POST, initial={"option": "10"})
https://docs.djangoproject.com/en/1.10/topics/forms/modelforms/#providing-initial-values
I had this other solution (I'm posting it in case someone else as me is using the following method from the model):
class onlyUserIsActiveField(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(onlyUserIsActiveField, self).__init__(*args, **kwargs)
self.fields['is_active'].initial = False
class Meta:
model = User
fields = ['is_active']
labels = {'is_active': 'Is Active'}
widgets = {
'is_active': forms.CheckboxInput( attrs={
'class': 'form-control bootstrap-switch',
'data-size': 'mini',
'data-on-color': 'success',
'data-on-text': 'Active',
'data-off-color': 'danger',
'data-off-text': 'Inactive',
'name': 'is_active',
})
}
The initial is definded on the __init__ function as self.fields['is_active'].initial = False
As explained in Django docs, initial is not default.
The initial value of a field is intended to be displayed in an HTML . But if the user delete this value, and finally send back a blank value for this field, the initial value is lost. So you do not obtain what is expected by a default behaviour.
The default behaviour is : the value that validation process will take if data argument do not contain any value for the field.
To implement that, a straightforward way is to combine initial and clean_<field>():
class JournalForm(ModelForm):
tank = forms.IntegerField(widget=forms.HiddenInput(), initial=123)
(...)
def clean_tank(self):
if not self['tank'].html_name in self.data:
return self.fields['tank'].initial
return self.cleaned_data['tank']
If you want to add initial value and post other value you have to add the following :
or None after request.POST
form = JournalForm(request.POST or None,initial={'tank': 123})
If you want to add files or images also
form = JournalForm(request.POST or None,request.FILES or None,initial={'tank': 123})
I hope this can help you:
form.instance.updatedby = form.cleaned_data['updatedby'] = request.user.id
I also encountered the need to set default values in the form during development. My solution is
initial={"":""}
form=ArticleModel(request.POST)
if form.has_changed():
data = {i: form.cleaned_data[i] for i in form.changed_data}
data.update({key: val for key, val in init_praram.items() if key not in form.changed_data})
use form.has_changed ,if form.fields is required you can use this method
How I added the initial to the form:
I read #Sergey Golovchenko answer.
So I just added it to the form in if request.method == 'POST':.
But that's not where you place it, if you want to see what value it got before posting the form.
You need to put it in the form where the else is.
Example here from views.py
def myForm(request):
kontext = {}
if request.method == 'POST':
# You might want to use clean_data instead of initial here. I found something on a stack overflow question, and you add clean data to the Forms.py, if you want to change the post data. https://stackoverflow.com/questions/36711229/django-forms-clean-data
form = myModelForm(request.POST, initial={'user': request.user})
if form.is_valid():
form.save()
return redirect('/')
else:
# you need to put initial here, if you want to see the value before you post it
form = myModelForm(initial={'user': request.user})
kontext['form'] = form
return render(request, 'app1/my_form.html', kontext)

Django forms initial value user_full_name

Is the any solution to get django's user_full_name as a initial value for form? My idea was to display a django's form on the end of shopping to finish a order. I want also do put into a form total value, but this is for later.
I did something like this:
user_dane = request.user.get_full_name
koszyk = request.session.get('koszyk', [])
produkty = list(Produkt.objects.filter(pk__in=koszyk))
suma_cen = Produkt.objects.filter(pk__in=koszyk).aggregate(suma=Sum('cena'))
suma_wszystkich_cen = suma_cen['suma']
form=ZamowienieForm(initial={'imie_nazwisko':user_dane, 'kwota_do_zaplaty':suma_wszystkich_cen})
but this is working only when request.method is POST.
if request.method =='POST':
form = ZamowienieForm()
According to documentation I shouldn't initial a empty form with POST... Is there any chance to have a user full name into a form?
Here is the form class:
class ZamowienieForm(forms.ModelForm):
class Meta:
model = Zamowienie
fields = ('imie_nazwisko', 'kwota_do_zaplaty', 'miejscowosc',
'ulica','numer_domu', 'numer_mi‌​eszkania', 'kod_pocztowy',)
class NewMeta:
readonly = ('imie_nazwisko','kwota_do_zaplaty',)
Maybe try something like this inside ZamowienieForm class
def __init__(self, *args, **kwargs):
super(ZamowienieForm, self).__init__(*args, **kwargs)
self.fields['imie_nazwisko'] = self.initial.get('imie_nazwisko')
self.fields['kwota_do_zaplaty'] = self.initial.get('kwota_do_zaplaty')
Although I don't understand why "initial" is not working out of the box
In this case, you only need to initialize your form once, and not inside a conditional check if the request is a GET or POST:
def your_view(request):
form = ZamowienieForm(
request.POST or None,
initial={'imie_nazwisko': request.user.get_full_name(),
'kwota_do_zaplaty': suma_wszystkich_cen}
)
if request.method == 'POST' and form.is_valid():
# do whatever
This way you are always passing in the initial value, and if request.method == 'GET', then None is passed as the first positional argument to the form.
Also, user.get_full_name is an instance method, not a property, so using request.user.get_full_name only returns the bound method, not the actual value. You have have to call the function using ()
Finally, this will only work for users that are authenticated. The anonymous user object in Django won't return any user-specific information.

Django MultipleChoiceField choices of users throws error

I am trying to write a form that allows the user to select as many users from a specific group as they want. However when I try to use the list of users as an option I get an error saying that 'User' object does not support indexing.
Its a fairly standard form, the main difference is that the group is filtered based on a kwarg passed to the form. The form is passed a project_id (project object primary key) and it then finds the group associated with that project and generates the field.
From forms.py
class ModifyTeamForm(forms.Form):
action = ChoiceField(choices=[('remove', 'Remove users'), ('promote', 'Promote to lead.')])
def __init__(self, *args, **kwargs):
# The project to get the team for
project_id = kwargs.pop('project_id', None)
super(ModifyTeamForm, self).__init__(*args, **kwargs)
project = Project.objects.get(pk=project_id)
# Team for this project
team = User.objects.filter(groups__name=project.project_name)
# Create a form field to select current team members
current_team = MultipleChoiceField(required=True, choices = team, widget=CheckboxSelectMultiple)
# Add the field
self.fields['current_team'] = current_team
My views.py
#login_required
def team(request, project_id):
if request.method == "POST":
# Not yet implemented
return
else:
form = ModifyTeamForm(project_id=project_id)
template = loader.get_template('projects/team.html')
context = RequestContext(request, {
'form': form,
})
return HttpResponse(template.render(context))
It's because MultipleChoiceField.choices is expected to be a 2d Array effectively (https://docs.djangoproject.com/en/1.7/ref/forms/fields/#django.forms.ChoiceField.choices).
So you could do something like this:
team = [(u.pk, u.email) for u in User.objects.filter(groups__name=project.project_name)]
And that will return you a list continaing the combintation of
[('user1.pk', 'user1.email'), ('user2.pk', 'user2.email'),...]
which will be useable as the choices.

How to update DjangoItem in Scrapy

I've been working with Scrapy but run into a bit of a problem.
DjangoItem has a save method to persist items using the Django ORM. This is great, except that if I run a scraper multiple times, new items will be created in the database even though I may just want to update a previous value.
After looking at the documentation and source code, I don't see any means to update existing items.
I know that I could call out to the ORM to see if an item exists and update it, but it would mean calling out to the database for every single object and then again to save the item.
How can I update items if they already exist?
Unfortunately, the best way that I found to accomplish this is to do exactly what was stated: Check if the item exists in the database using django_model.objects.get, then update it if it does.
In my settings file, I added the new pipeline:
ITEM_PIPELINES = {
# ...
# Last pipeline, because further changes won't be saved.
'apps.scrapy.pipelines.ItemPersistencePipeline': 999
}
I created some helper methods to handle the work of creating the item model, and creating a new one if necessary:
def item_to_model(item):
model_class = getattr(item, 'django_model')
if not model_class:
raise TypeError("Item is not a `DjangoItem` or is misconfigured")
return item.instance
def get_or_create(model):
model_class = type(model)
created = False
# Normally, we would use `get_or_create`. However, `get_or_create` would
# match all properties of an object (i.e. create a new object
# anytime it changed) rather than update an existing object.
#
# Instead, we do the two steps separately
try:
# We have no unique identifier at the moment; use the name for now.
obj = model_class.objects.get(name=model.name)
except model_class.DoesNotExist:
created = True
obj = model # DjangoItem created a model for us.
return (obj, created)
def update_model(destination, source, commit=True):
pk = destination.pk
source_dict = model_to_dict(source)
for (key, value) in source_dict.items():
setattr(destination, key, value)
setattr(destination, 'pk', pk)
if commit:
destination.save()
return destination
Then, the final pipeline is fairly straightforward:
class ItemPersistencePipeline(object):
def process_item(self, item, spider):
try:
item_model = item_to_model(item)
except TypeError:
return item
model, created = get_or_create(item_model)
update_model(model, item_model)
return item
I think it could be done more simply with
class DjangoSavePipeline(object):
def process_item(self, item, spider):
try:
product = Product.objects.get(myunique_id=item['myunique_id'])
# Already exists, just update it
instance = item.save(commit=False)
instance.pk = product.pk
except Product.DoesNotExist:
pass
item.save()
return item
Assuming your django model has some unique id from the scraped data, such as a product id, and here assuming your Django model is called Product.
for related models with foreignkeys
def update_model(destination, source, commit=True):
pk = destination.pk
source_fields = fields_for_model(source)
for key in source_fields.keys():
setattr(destination, key, getattr(source, key))
setattr(destination, 'pk', pk)
if commit:
destination.save()
return destination

Categories

Resources