the insert works but multiple data enters, when two data are inserted when I try to update, it does not update the record
I just want that when the data is already have record in the database, just update. if not, it will insert
def GroupOfProduct(request):
global productOrderList
relatedid_id = request.POST.get("relatedid")
groups = ProductRelatedGroup(id=id)
productOrderList=[]
try:
for products_id in request.POST.getlist("product"):
products = Product(id=products_id)
insert_update_groupofproduct = ProductRelatedGroupAndProduct(
product = products
)
insert_update_groupofproduct.save()
except ProductRelatedGroupAndProduct.DoesNotExist:
for products_id in request.GET.getlist("relatedid"):
products = Product(id=products_id)
insert_update_groupofproduct = ProductRelatedGroupAndProduct.objects.get(id=products)
insert_update_groupofproduct.product = products
insert_update_groupofproduct.save()
return redirect(relatedgroup)
this is my models.py
class Product(models.Model):
product = models.CharField(max_length=500)
class ProductRelatedGroup(models.Model):
category = models.CharField(max_length=500, blank=True)
class ProductRelatedGroupAndProduct(models.Model):
productrelatedgroup = models.ForeignKey(ProductRelatedGroup,on_delete=models.SET_NULL, null=True, blank=True,verbose_name="Product Related Group")
product = models.ForeignKey(Product,on_delete=models.SET_NULL, null=True, blank=True,verbose_name="Product")
UPDATE
I tried this, the insert works fine, but the update does not work
def GroupOfProduct(request):
global productOrderList
groups = ProductRelatedGroup(id=id)
idproduct = request.POST.get('relatedid')
if ProductRelatedGroupAndProduct.objects.filter(id=idproduct).exists():
print("update")
for products_id in request.GET.getlist("relatedid"):
products = Product(id=products_id)
insert_update_groupofproduct = ProductRelatedGroupAndProduct.objects.get(id=products)
insert_update_groupofproduct.product = products
insert_update_groupofproduct.save()
return redirect(relatedgroup)
else:
productOrderList = []
for isa in request.POST.getlist('relatedid'):
productOrderList.append(isa)
i = 0
for i in productOrderList:
for products_id in request.POST.getlist("product"):
products = Product(id=products_id)
insert_update_groupofproduct = ProductRelatedGroupAndProduct(
productrelatedgroup=groups,
product=products
)
insert_update_groupofproduct.save()
return redirect(relatedgroup)
return redirect(relatedgroup)
FLOW OF MY PROGRAM (with picture)
when the admin-user Insert data just (like this)
the batch insert work perfectly fine
and when i tried to update (batch update)
only one data updated
and when i tried to insert again (just like this)
no result
In Insert
<QueryDict: {'csrfmiddlewaretoken': ['F3evgRJwNw4p5XCOVE0qeFhP3gmGG5ay4GBbpoZQg3P5l6TNXHY7KN2lD56s6NCU'], 'relatedid': ['200', '201']}>
This is my html,
{% for relatedgroups in relatedgroups %}
<input type="hidden" name="relatedid" value="{{relatedgroups.id}}">
{% endfor %}
<fieldset class="module aligned ">
<div class="form-row field-user_permissions">
<div>
<div class="related-widget-wrapper">
<select name="product" id="id_user_permissions" multiple class="selectfilter" data-field-name="Product" data-is-stacked="0">
{% for relatedgroups in relatedgroups %}
<option value="{{relatedgroups.product.id}}" selected>{{relatedgroups.product}}</option>
{% endfor %}
{% for product in products %}
<option value="{{product.id}}">{{product.id}}-{{product}}</option>
{% endfor %}
</select>
</div>
</div>
</div>
</fieldset>
when the data is selected or already have data in the database it will shows in Chosen Product box
Please use update_or_create method. This method if a data is exist then updated the details else newly inserted.
Reference:
https://www.kite.com/python/docs/django.db.models.QuerySet.update_or_create
https://djangosnippets.org/snippets/1114/
def GroupOfProduct(request):
group_id = request.POST.get('group')
groups = ProductRelatedGroup(id=group_id)
idproduct = request.POST.get('product')
for products_id in request.POST.getlist("product"):
products = Product(id=products_id)
ProductRelatedGroupAndProduct.objects.update_or_create(id=idproduct,defaults={'product':products,'productrelatedgroup':groups})
return redirect(relatedgroup)
We need to delete all the records first from ProductRelatedGroupAndProduct using the related_ids (product ids which were already selected) and then enter again into ProductRelatedGroupAndProduct using product_ids (product ids which are selected).
Why we need to delete ?
In a situation where you want to remove a product from the selected list, an update would not delete the relation. In-order to remove that product from the selected list we need to delete that relation (not the Product).
def GroupOfProduct(request):
related_ids = request.POST.getlist('relatedid')
product_ids = request.POST.getlist("product")
# delete the existing ProductRelatedGroupAndProduct entries
for product_id in related_ids:
product = Product.objects.filter(id=product_id).first()
obj = ProductRelatedGroupAndProduct.objects.get(product = product)
obj.delete()
# create new records in ProductRelatedGroupAndProduct with the selected product_ids
for product_id in product_ids:
product = Product.objects.filter(id=product_id).first()
obj = ProductRelatedGroupAndProduct(
product = product,
)
obj.save()
Related
I have a page on my Django website, which is displaying a number of tables based on information stored in the database.
The view being used to create the page displaying the tables is defined with:
def current_budget(request, budget_id):
""" View the active provisional/deposit budget """
budget = Budget.objects.select_related('project', 'project__budget_overview').prefetch_related('project__projectroom_set', 'project__budget_versions', 'budget_items').get(id=budget_id)
project = budget.project
# project.projectroom_set.filter(budgetitem__isnull=True, cciitem__isnull=True).delete()
if project.budget_overview.deposit_budget_saved: return HttpResponseRedirect(reverse('costing:combined_budget', args=[project.id]))
#This is now done in the costing_home view
# if not budget:
# Budget.objects.create(project=project, current_marker=1)
if not budget.budget_items.exists() and not project.budget_overview.deposit_budget_saved: init_budget(budget) # Create initial BudgetItem objects as standard
budget_items = budget.budget_items.select_related('budget', 'budget__project', 'project_room', 'project_room__room', 'room')#.order_by('build_type', 'build_type_detail', 'project_room', 'order') # .exclude(build_type=None)
budget_items2 = None #budget.budget_items.filter(build_type=None).order_by('build_type_detail', 'project_room', 'room')
context = {
'project': project,
'budget': budget,
'offset1': -5,
'offset2': -4,
}
try: context['current_budget'] = project.budget_versions.get(current_marker=1) #For option name/date on top of pdfs
except ObjectDoesNotExist: pass
if request.GET.get('version') or project.budget_overview.deposit_budget_saved: #Make the fields all readonly
context['readonly'] = True
context['offset1'] = -7
if request.GET.get('report'): #Schedule of works report uses same data as current budget form
""" Client view of budget. IMPORTANT: Hidden items are not displayed here """
items_grouped = groupby(budget_items.filter(hidden_cost=False), lambda x: x.build_type)
grouped_items = [(x, list(y)) for x, y in items_grouped]
context['grouped_items'] = grouped_items
if request.GET.get('pdf'):
template = get_template('costing/report_schedule_of_works.html')
html = template.render(context)
file = open('test.pdf', "w+b")
pisaStatus = pisa.CreatePDF(html.encode('utf-8'), link_callback=fetch_resources, dest=file,
encoding='utf-8')
file.seek(0)
pdf = file.read()
file.close()
return HttpResponse(pdf, 'application/pdf')
else:
context['webview'] = 1
context['html'] = render_to_string('costing/report_schedule_of_works.html', context)
context['active_tab'] = '3'
return render(request, 'costing/reports_pre_deposit.html', context)
else:
if not context.get('readonly'):
context['skill_day_rate'] = skill_day_rate
context['labour_day_rate'] = labour_day_rate
# Dict with ProjectRoom ids and the total for the room
room_totals = {}
for project_room in project.projectroom_set.all():
room_totals[project_room.id] = sum(item.total_inc_profit for item in budget_items if item.project_room == project_room)
context['room_totals'] = room_totals
item_formset = BudgetItemFormset(queryset=budget_items, form_kwargs={'project': project})
item_form = item_formset.forms[0]
context['field_count'] = len(item_form.visible_fields())
context['ao_field_count'] = len(item_form.visible_fields())
room_choices = project.room_choices
context['formset'] = item_formset
context['widths'] = budget_item_column_widths #First column is add/delete options to allow for forloop count offset
context['options_width'] = options_width #First column is add/delete options to allow for forloop count offset
context['labour_rate'] = labour_day_rate
context['skill_rate'] = skill_day_rate
context['item_code_options'] = ItemCodeForm()
skill_total = int(budget_items.aggregate(Sum('skill_days'))['skill_days__sum'] or 0)
if budget_items2: labour_total = int(budget_items2.aggregate(Sum('labour_days'))['labour_days__sum'])
else: labour_total = 0
return render(request, 'costing/budget_current.html', context)
I now want to add a 'custom' column to these tables, to allow the user to enter their own notes in (i.e. one that will not be displaying data retrieved from the database, but which should add information to each row in the database when it is saved).
How would I do this? I would have thought that I will do it in the view (Python), rather than in the template (HTML), since the view is where the tables are being constructed?
Edit
So, I've added the extra field to the model in models.py for the app:
class Deposit(models.Model):
""" Each Deposit is linked to a Payment, whose is_booking_deposit field = True """
project = models.OneToOneField('projects.Project', null=True, blank=True)
half_paid = models.DateField(null=True, blank=True)
date_received = models.DateField(null=True, blank=True)
amount_exc_vat = models.DecimalField(decimal_places=2, max_digits=12, null=True, blank=True)
invoice_raised = models.DateField(null=True, blank=True)
notes = models.TextField(null=True, blank=True)
# Create a column for 'custom notes' in the tables:
custom_notes = models.TextField(null=True, blank=True) #Add the column to table in tables.py
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
payment = models.OneToOneField(Payment, null=True)
...
The field that I've added to the Deposit model above is the custom_notes one.
I also tried adding the same field to the BudgetItemTable() in tables.py:
class BudgetItemTable(tables.Table):
# Add a column for 'custom notes' to the tables for current budget
custom_notes = tables.Column(accessor='self.custom_notes')
class Meta:
model = BudgetItem
attrs = {"class": "paleblue"}
exclude = ('id')
I've run python manage.py makemigrations & python manage.py migrate myApp, but when I refresh my browser to view this page again, the new column for my table is not displayed- do I need to add it to the view somehow? How would I do this?
Edit
The HTML file for the view that displays the table I want to add a column to has the following structure:
...
{% block page_options_style %}allow-footer{% endblock page_options_style %}
{% block page_options %}
...
{% block report %}
<div id='budget_form' class="col-12" data-pr="{{project.id}}" style="margin-bottom:7em;">
<form class="autosave_form formset num_refresh text-sm" data-view-url="{% url 'costing:save_items' budget.id %}">{% csrf_token %}
{{formset.management_form}}
<div>
<table ...>
<thead class=... >
<tr class=...>
...
<!-- code to get table headings here -->
<th style="width:{{options_width}}">Custom Notes</th>
<!-- I added the 'Custom Notes' heading myself -->
</tr>
</thead>
...
<tbody>
...
<tr id="item_{{forloop.counter}}" class="{% cycle 'odd' 'even' %}">
...
<!-- code add columns and populate rows here -->
<td>
<a class="delete" ... ></a>
<!-- I can see this column with the 'delete' values is the last column in the table on the webpage, so I want to manually add another column after it in the table- I tried doing this by adding the following tags: -->
</td>
<td>
<a class="custom_notes" type="text" value=""></a>
</td>
...
</tr>
</tr>
</tbody>
</table>
...
</tbody>
</table>
</div>
</form>
I tried to add the 'Custom Notes' column to the table with the lines:
<td>
<a class="custom_notes" type="text" value=""></a>
</td>
When I view this page in the browser, I can see the Custom Notes column heading that I've added, displayed above the table, but it is displayed just to the right of where the table ends (I expected the column that I added to the table to be displayed below it).
The table column for 'custom notes' that I have tried adding is not displayed in the table at all...
Why is this? How can I get the new column to be displayed in the table on the webpage? I've added the corresponding field to the model in models.py, so although there won't be any data in this field for any of the items in the database at the moment, when the user enters data in this field in the table, there is somewhere for those values to be stored in the model in the database...
Edit
I managed to add this additional field to the table by appending the following HTML to the table, just inside the last two </tr> tags, inside </tbody></table>:
<td>
<a class="custom_notes" type="text" value=""></a>
<input id="budget_notes" type="text" width="100" value="">
</td>
But for some reason, the text input field that I have added is very narrow- you can't see more than a few characters of text at a time when typing into it. I tried specifying the width of the field using width="100", as shown above, but this doesn't appear to have made any difference. How can I force the cells in this column to have a set width?
I want to add to the cart the actual product I'm in (product_detail.html).
So in the product_unit, is just needed to specify the quantity of the product.
Anyway I can't make the unit_product, automatically add the actual product I'm in.
forms.py
class Product_unitForm(forms.ModelForm):
class Meta:
model = Product_unit
fields = [
'product',
'quantity',
]
widgets = {'product': forms.HiddenInput()}
I hide the product from the template, because it is just the actual product, no need to specify.
views.py
def product_detail(request, id_category=None,id_product=None):
actual_product = Product.objects.get(id = id_product)
#Has an actual customer
#FORM
form_product_unit = Product_unitForm(request.POST or None)
form_product_unit.fields['product'] = actual_product # I try to add the product this way
if form_product_unit.is_valid():
instance_product_unit = form.save(commit=False)
instance_product_unit.product.save()
last_order = Order.objects.last()
is_buying = False
if(last_order.status == "en curso"):
is_buying = True
context = {
"Product" : actual_product,
"Is_buying" : is_buying,
#FORMS
"form_product_unit" : form_product_unit,
}
return render(request, "shopping/product_detail.html", context)
I want to manually from the views, add the product field of product_unit to the actual product it has (actual_product)
template
<img src="{{Product.image.url}}"/>
<h1>{{Product.title}}</h1>
<form method="POST" action="">{% csrf_token %}
{{ form_product_unit.as_p }}
<input type="submit" value="Add" />
</form>
In your views.py file I think you just need to make two changes
def product_detail(request, id_category=None,id_product=None):
actual_product = Product.objects.get(id = id_product)
form_product_unit = Product_unitForm(data=request.POST or None,
initial={'product': actual_product})
And also remove the line form_product_unit.fields['product'] = actual_product. You might need to play around with the initial dictionary a bit to get it to bind the correct value to the field but that's the general idea. The related section in the docs is https://docs.djangoproject.com/en/1.9/ref/forms/api/#dynamic-initial-values
I have two models
Room and RoomLog
each single Room object can have multiple RoomLog objects.
the purpose of it is that Room object has got an attribute: value and method save(). I create a new related RoomLog object every time a user saves changed attribute of any specific Room object and saves it.
RoomLog object has got value attribute and date attribute.
date is related to Room save method so it gives Room changed value save DATE.
My question is:
Q1: How to return all unique days from all RoomLog objects so I know when any save took place ?
Q2: Let's say we know how to return unique days. So, the question is: how to select any day from these unique days and display all Room objects values by chosen date ? I would like to return last saved value from chosen date for each Room object.
The way I tackle both questions at the moment (I am looking for more Pythonic, faster, better performing solutions) is:
I created Form in which I iterate through RoomLog objects:
class MyForm(forms.Form):
roomdates = []
roomextracted = []
for r in RoomLog.objects.all():
if r not in roomdates:
roomdates.append(r.update_date)
for i in roomdates:
if i not in roomextracted:
roomextracted.append(i)
ROOMDATA = [(r, r) for r in roomextracted]
my_choice_field = forms.ChoiceField(choices=ROOMDATA)
then I have a view to pass selected date to another view in which I filter Room.objects.all() by selected date:
def choices(request):
form = RoomLogChoices()
form.fields['choice'].choices = list()
testlist = []
for rl in RoomLog.objects.all():
if rl.update_date not in testlist:
testlist.append(rl.update_date)
for d in testlist:
form.fields['choice'].choices.append(
(
d,d
)
)
return render(request, 'prostats/choices.html', {'form':form})
next I have choicesform.html in which I select date from dropdown menu:
{% extends "base.html" %}
{% block content %}
<form action="" method="post" >
{% csrf_token %}
<ul>
{% for choice in form.my_choice_field.field.choices %}
<li>
<input type="radio" name="my_choice_field" value="{{choice.0}}"
{% if equal form.my_choice_field.data choice.0 %}
checked="checked"
{% endifequal %}/>
<label for="">{{choice.1}}</label>
</li>
{% endfor %}
</ul>
<input type="submit" value="Submit" />
</form>
{% endblock %}
and this is the view in which I handle POST data
class AllRoomsView(ListView):
template_name = 'prostats/roomsdetail.html'
queryset = Room.objects.all()
def get_context_data(self, **kwargs):
context = super(AllRoomsView, self).get_context_data(**kwargs)
context['rooms'] = Room.objects.all()
context['rlog'] = RoomLog.objects.all()
roomsdates = []
for r in context['rlog']:
if r not in roomsdates:
roomsdates.append(r.update_date)
roomextracted = []
for i in roomsdates:
if i not in roomextracted:
roomextracted.append(i)
context['roomextracted'] = roomextracted
choicedate = self.request.GET.get('choice')
if choicedate != None:
choosend = choicedate
else:
choosend = '2016-02-01'
#filtered rlogs
rlfilteredempty = []
for r in context['rooms']:
i = RoomLog.objects.filter(room=r.id, update_date__lte = choosend).order_by('-update_date')[:1]
if i:
rlfilteredempty.append(i[0])
else:
rlfilteredempty.append(r)
context['rlfiltered'] = rlfilteredempty
context['choicedate'] = self.request.GET.get('choice')
#context['roomfiltersettime'] = RoomLog.objects.filter(update_date__lte = choosend)
context['roomfiltersettime'] = RoomLog.objects.filter(update_date__lte = choosend)
rslice = []
for r in context['rooms']:
i = RoomLog.objects.filter(room=r.id, update_date__lte = choosend).order_by('-update_date')[:1]
if i:
for rsobject in i:
rs = (r.flat.flat_number,r.flat.block.block_name,r.room_name)
rl = rsobject.id
rv = rsobject.room_value
rslice.append((rs,rl,rv))
else:
rs = (r.flat.flat_number,r.flat.block.block_name,r.room_name)
r = r.id
rslice.append((rs,r))
context['rslice'] = rslice
So, all of it what I have done I feel is not very good and maybe somebody can point me with some good ideas on how to tackle this problem better ?
EDIT: update of the post with my Room and RoomLog models:
class Room(models.Model):
room_name = models.CharField(max_length= 10)
room_value = models.PositiveSmallIntegerField(default=0)
flat = models.ForeignKey(Flat)
created_date = models.DateField(auto_now_add= True)
created_time = models.TimeField(auto_now_add= True)
substage_name = models.CharField(max_length=50,default="")
def __init__(self, *args, **kwargs):
super(Room, self).__init__(*args, **kwargs)
self.value_original = self.room_value
def save(self, **kwargs):
with transaction.atomic():
response = super(Room, self).save(**kwargs)
if self.value_original != self.room_value:
room_log = RoomLog()
room_log.room = self
room_log.room_value = self.value_original
room_log.save()
return response
class RoomLog(models.Model):
room = models.ForeignKey(Room)
room_value = models.PositiveSmallIntegerField(default=0)
update_date = models.DateField(auto_now_add= True)
update_time = models.TimeField(auto_now_add= True)
To return all unique days, use distinct() on your created_date field. That will, of course, only work if created_date is actually a date and not a datetime value!
RoomLog.objects.all().distinct('created_date')
If your created value is a datetime, you need to make it a date first, using Django's func() and F() functions. That uses the DATE() SQL functions that may not work on all databases, but it does on Postgres and probably many others.
RoomLog.objects.all()\
.annotate(created_date=Func(F('created'), function='DATE'))\
.order_by('-created_date')\
.distinct('created_date')
Only a partial answer. The second question depends on the layout of your models that you didn't post.
My model:
class Category(models.Model):
name = models.CharField(max_length=50, unique=True)
order = models.SmallIntegerField()
My template:
{% for c in categories %}
{{ c }} -- <input type="text" name="order[{{ c.id }}]" value="{{ c.order }}">
Submit Button
{% endfor %}
My view:
def category_order(request):
print request.POST
print request.POST.getlist('order[]')
Output:
<QueryDict: {u'order[3]': [u'1'], u'order[1]': [u'1'], u'order[5]': [u'2'], u'order[4]': [u'33'], u'order[2]': [u'2'], u'order[6]': [u'3'], u'csrfmiddlewaretoken': [u'4XjehEwMdNK032342JkYJvBJabunKB'], u'order-btn': [u'order']}>
[]
So my question is how can I loop over every order[N] and get the category ID and its new order value to save it to the database?
Using dict comprehension:
orders = {name.split('[')[1].split(']')[0]: request.POST[name]
for name in request.POST if name.startswith('order[')}
# `orders` is not a dictionary maps `id`s to `order`s.
# Do something with `orders`.
Or using simple for loop:
for name in request.POST:
if not name.startswith('order['): continue
id_ = name.split('[')[1].split(']')[0]
order = request.POST[name]
# Do something with `id_`, `order`
If you are looking at having ordered objects, there is a library, django-ordered-model , that can take care of that. That includes a good, drag-and-drop admin view. For example:
from django.db import models
from ordered_model.models import OrderedModel
class Category(OrderedModel):
name = models.CharField(max_length=50, unique=True)
class Meta(OrderedModel.Meta):
pass
I have a content that I'd like to rate on multiple criteria.
Imagine this kind of model:
class Content(models.Model):
name = models.CharField(max_length=50)
class Criterion(models.Model):
name = models.CharField(max_length=50)
content = models.ForeignKey(Content)
class ContRate(models.Model):
user = models.ForeignKey(User, help_text="Who rated ?")
crit = models.ForeignKey(Criterion)
rate = models.DecimalField()
The user has a page displaying the content.
From this page, he can also rate the content on the criteria set
The rating will be done with Ajax.
Now I'm trying to implement the view & the template
view.py
#...
def viewcont(request, content_id):
"""The user can view a content & rate it"""
content = get_object_or_404(Content, pk=content_id)
RateFormSet = modelformset_factory(ContRate)
formset = RateFormSet(queryset=ContRate.objects.filter(content=content, user=request.user))
objs = {
'content': content,
'forms': formset,
}
return render_to_response('content/content_detail.html', objs
, context_instance=RequestContext(request)
)
#...
content_detail.html
<!-- ... -->
<div id="rating">
<ul>
{% for crit in content.crit_set.all %}
<li>
{{ crit }}
<div class="rateit"
data-rateit-value="the_actual_rating_if_already_there"
data-rateit-ispreset="true"
crit-id="{{ crit.id }}"></div>
</li>
{% endfor %}
</ul>
</div>
<!-- ... -->
Now how can I use the forms formset to display the actual rates ?
And how can I draw an empty form to be posted by Ajax from any clicked star ?
(I know the javascript/jQuery part)
Not sure what the point of the formset is here. The rates are all available via the criteria object, using the reverse foreign key to ContRate in exactly the same way as you've done from Criteria to Content.
To make this as efficient as possible, you probably want to get the relevant ratings in the view and bring them together into a single datastructure:
content = get_object_or_404(Content, pk=content_id)
criteria = content.criteria_set.all()
user_ratings = ContRate.objects.filter(content=content, user=request.user)
ratings_dict = dict((c.crit_id, c.rate) for c in user_ratings)
for crit in criteria:
crit.user_rating = ratings_dict.get(crit.id)
Now you can pass criteria directly to your template, and there you can iterate through it to show the user_rating for each one.
(Final point: "criteria" is plural, the singular is "criterion". :-)