Django -- Removing an option from queryset form field if already selected - django

I have a form field that calls a queryset and uses the 'select' widget.
Is there a way to remove an option value from the queryset if it already has been 'added' to the cart?
In the select form, there's three options: Option A, Option B, Option C.
The user selects Option A, and clicks 'Add'. Now once the user clicks 'Add', I want to remove Option A from the select.
Only Option B and Option C will be available to choose from.
Can this be done just using Django+Python? Or will I need to use additional JS/jQuery?
Thanks!
models.py
class Pickup(models.Model):
# id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=False, null=True)
total = models.DecimalField(max_digits=100, decimal_places=2, default=0.00)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
active = models.BooleanField(default=True)
status = models.CharField(max_length=120, choices=STATUS_CHOICES, default="Open")
def __str__(self):
return "Pickup Order ID: %s" %(str(self.id))
class PickupItem(models.Model):
pickup = models.ForeignKey('Pickup', null=True, blank=True)
dropoffitem = models.ForeignKey(DropoffItem)
notes = models.TextField(null=True, blank=True)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
forms.py
class AddPickupItemForm(forms.ModelForm):
dropoffitem = forms.ModelChoiceField(queryset=DropoffItem.objects.all(), widget=forms.Select(attrs={'class':'form-control'}))
class Meta:
model = PickupItem
fields = ['dropoffitem']
views.py
def add_item_to_pickup_order(request):
request.session.set_expiry(120000)
try:
user = request.user
the_id = request.session['pickup_id']
pickup = Pickup.objects.get(id=the_id)
except:
user = request.user
new_pickup_order = Pickup(user=user)
new_pickup_order.save()
request.session['pickup_id'] = new_pickup_order.id
the_id = new_pickup_order.id
pickup = Pickup.objects.get(id=the_id)
try:
dropoffitem = DropoffItem.objects.get(id=id)
except DropoffItem.DoesNotExist:
pass
except:
pass
form = AddPickupItemForm(request.POST or None)
if request.method == "POST":
dropoffitem_id = int(request.POST['dropoffitem'])
pickup_item = PickupItem.objects.create(pickup=pickup, dropoffitem_id=dropoffitem_id)
pickup_item.save()
return HttpResponseRedirect('%s'%(reverse('add_item_to_pickup_order')))
context = {
"pickup": pickup,
"form": form,
}
return render(request, 'pickups/create_pickup_order.html', context)
.html
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="container">
<div class="col-xs-12">
<h1>Create Cart</h1>
<form method="POST" action="{% url 'add_item_to_pickup_order' %}">
{% csrf_token %}
<table class="table">
<thead>
<th>Item</th>
<th></th>
</thead>
<tr>
<td>{{ form.dropoffitem }}</td>
<td><input type="submit" value="Add Item" class="btn btn-default btn-primary" /></td>
</tr>
</table>
</form>

To exclude/remove an item from the queryset, you can use exclude.
YourModel.objects.exclude(id=4)
To exclude multiple items:
YourModel.objects.exclude(id__in=[4, 6, 10])
More info about exclude on Django docs.

Related

Reviews showing on the reviewer's own profile page instead of the profile that it was written on

I am adding a simple review function on my Social platform Django project, where Users can write a review on another user's profile. But after posting the review, it's only showing on the profile page of the user that I'm currently signed in to.
This is my models.py
` class Review(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
reviewmsg = models.TextField(null=True, blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.reviewmsg)`
views.py
` def userProfile(request, pk):
user = User.objects.get(id=pk)
rooms = user.room_set.all()
reviews = user.review_set.all()
if request.method == 'POST':
review = Review.objects.create(
user=request.user,
reviewmsg=request.POST.get('reviewmsg')
)
return redirect('user-profile', pk=user.id)
context = {'user':user, 'rooms':rooms, 'reviews':reviews}
return render(request, 'pages/profile.html', context)`
excerpt from my profile page template
` <div>
{% for review in reviews %}
<div class="comment-sect" style="margin-top: 0.5rem;">
<div class="comment-photo" style="margin: auto;">
<a href="{% url 'user-profile' review.user.id %}">
<div class="profile-photo">
<img src="{{review.user.avatar.url}}">
</div>
</a>
</div>
<div class="comment-info" style="width: 300px;">
<small>#{{review.user}}</small>
<small>{{review.created|timesince}} ago </small>
<p style="margin-bottom:0;">{{review.reviewmsg}}</p>
</div>
</div><!--end of comment-sect-->
{% endfor %}
<div class="comment-form" style="margin-top: 0.5rem; text-align:center;">
<form method="POST" action="">
{% csrf_token %}
<div>Submit a Review
<input type="text" name="reviewmsg" placeholder="What do you think about this User?"/>
</div>
</form>
</div>`
EDITED
As mentioned by #lain Shelvington, I agree that I need to add another ForeignKey to my models. I tried updating the models.py to:
user = models.ForeignKey(User, on_delete=models.CASCADE)
class Review(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
profile = models.ForeignKey(Profile, on_delete=models.SET_NULL, null=True)
reviewmsg = models.TextField(null=True, blank=True)
updated = models.DateTimeField(auto_now=True)
created = models.DateTimeField(auto_now_add=True)
def __str__(self):
return str(self.reviewmsg)
and views.py to:
user = User.objects.get(id=pk)
rooms = user.room_set.all()
reviews = user.review_set.all()
if request.method == 'POST':
review = Review.objects.create(
user=request.user,
profile=user,
reviewmsg=request.POST.get('reviewmsg')
)
return redirect('user-profile', pk=user.id)
context = {'user':user, 'rooms':rooms, 'reviews':reviews}
return render(request, 'pages/profile.html', context)
but ended up with error:
ValueError at /profile/7/
Cannot assign "<User: celeste>": "Review.profile" must be a "Profile" instance

Populate dropdown in template from model choices using Django Rest Framework

I have the model, serializer, viewset and html as follows:
GENDER = (
('MALE', 'Male'),
('FEMALE', 'Female'),
('OTHERS', 'Others'),
)
class Client(BaseModel):
first_name = models.CharField(max_length=256)
last_name = models.CharField(max_length=256, default="", blank=True, null=True)
designation = models.CharField(max_length=256, default="", blank=True, null=True)
gender = models.CharField(max_length=20, choices=GENDER, null=True, blank=True)
class ClientSerializer(QueryFieldsMixin, DynamicFieldsModelSerializer):
name = serializers.SerializerMethodField()
def get_name(self, obj):
return getattr(obj, "first_name", "") + " " + getattr(obj, "last_name", "")
class Meta:
model = Client
fields = '__all__'
#method_decorator(login_required, name='dispatch')
class ClientViewSet(viewsets.ModelViewSet):
model = Client
queryset = model.objects.all()
serializer_class = ClientSerializer
#action(detail=True, methods=['post','get'], renderer_classes=[renderers.TemplateHTMLRenderer])
def update_client(self, request, *args, **kwargs):
object = self.get_object()
context = {"operation": "Update",
"object_id": object.id,
"events": Event.GetEventsForObject(object)}
template_name = 'contact-client.html'
response = Response(context, template_name=template_name)
<form id="client-form" method="" action="" enctype="multipart/form-data" operation="{{operation}}">
{% csrf_token %}
<div class="row">
<div class="columns medium-5 medium-text-left">
<div class="select2-full select-2-full--sm input-rounded">
<label for = "gender" style="text-align: left;" >Gender</label>
<select id="gender" class="js-select2 input-height-sm element-contact" name="gender" validated="false"></select>
<option></option>
</div>
<div id="gender_error" style="display:none">
<p class="help-text"> <span class="form-action-icon error-icon"></span>Please select gender.</p>
</div>
</div>
<div class="columns medium-5 medium-text-left">
</div>
</div>
</form>
When I instantiate the ClientSerializer in shell like this ClientSerializer() then that gender field is shown along with its choices. But I am not able to show it in the template. All the other fields are being passed correctly.
How can I populate the dropdown with the values of choices in template? What should I pass in the option tag to display the choices and send the data to the view?
Use a ChoiceField instead of CharField
Replace this line
gender = models.CharField(max_length=20, choices=GENDER, null=True, blank=True)
with this one
gender = models.ChoiceField(max_length=20, choices=GENDER, null=True, blank=True)
To show all the choices in your template you can do something like this
from my_app.models import gender
context = {"choices" : gender}
response = Response(context,template_name)
And in template
{% for choice in choices %}
<option value="{{ choice }}">{{ choice }}</option>
{% endfor %}
If you are using the django-template then you can create a model form and pass that form as render context and then render that inside a form.
More on model form: https://docs.djangoproject.com/en/3.1/topics/forms/modelforms/

In Django getting errors based on dropdown selection

Getting error when fields are selected from the down. Not seeing why it is throwing error
Django Dropdown form.error: ERROR ALERT:
location_name Select a valid choice. That choice is not one of the available choices.
Here is the model, form, view and html looks like
MODEL
class Code (models.Model):
name = models.CharField(max_length=4, default=None, blank=True)
def __str__(self): return self.name
class Device (models.Model):
code = models.ForeignKey(Code, on_delete=models.CASCADE, null=True)
ip = models.GenericIPAddressField(protocol='IPv4', unique=True)
def __str__(self): return self.ip
class SiteData (models.Model):
site = models.ForeignKey(Code, on_delete=models.SET_NULL, null=True)
site_ip = models.ForeignKey(Device, on_delete=models.SET_NULL, null=True)
site_data1 = models.CharField(max_length=3, default='120')
class CombineData(models.Model):
location = models.ForeignKey(Code, on_delete=models.SET_NULL, null=True)
device = models.ForeignKey(AddData, on_delete=models.SET_NULL, null=True)
locdata = models.ForeignKey(SiteData, on_delete=models.SET_NULL, null=True)
FORM
class CombineData_form(forms.ModelForm):
class Meta:
model = P2_NexusLeafPair
fields = '__all__'
VIEW
def comboView(request, *args, **kwargs):
template_name = 'site_display.html'
code = Code.objects.order_by('-name')
device = Device.objects.order_by('-ip')
sitename = SiteData.objects.order_by('-site')
content = {
'code': code,
'device': device,
'sitename': sitename
}
if request.method == 'POST':
form = CombineData_form(request.POST or None)
print(form.is_valid())
#print(form.errors)
if form.is_valid():
. . .
else:
messages.error(request, form.errors)
else:
form = CombineData_form()
return render(request, template_name, content)
HTML
<form id="comboView" class="post-form" role=form method="POST" action="comboView">{% csrf_token %}
<div name="code" class="dropdown" id="mainselection" required>
<select name="dc_name">
<option class="dropdown-item" value="">---------</option>
{% for item in code %}
<option value="{{ item }}">{{ item }}</option>
{% endfor %}
</div>
<input type="submit" value="Submit" />
</form>
{same as other fields: device, sitename}
{% for item in content %}
{{item.code}}
{% endfor %}
Try that.

Django formset: file upload does not work

I am using Django to build a help desk and I want to allow the client to upload multiple files when they submit a ticket.
I am trying to do this by using a formset.
I have found many questions related to a similar problem, but I still have not been able to get my form working. I will appreciate a pointer in the right direction.
I have posted the relevant code below:
# models.py
class Ticket(models.Model):
PRIORITY_CHOICES = (
(1, 'Critical'),
(2, 'High'),
(3, 'Normal'),
(4, 'Low'),
(5, 'Very Low'),
)
STATUS_CHOICES = (
(1, 'Open'),
(2, 'Reopened'),
(3, 'Resolved'),
(4, 'Closed'),
(5, 'Duplicate'),
)
ticket_number = models.CharField(max_length=50, blank=True,
null=True, unique=True)
client = models.ForeignKey(settings.AUTH_USER_MODEL,
editable=True, on_delete=models.CASCADE,
related_name="tickets")
title = models.CharField("Summary",
max_length=200,
help_text='Provide a brief description of your request.')
description = models.TextField(blank=True,
help_text='Provide as much detail as possible to help us resolve this ticket as quickly as possible.')
due_date = models.DateField(blank=True, null=True)
assigned_to = models.ForeignKey(settings.AUTH_USER_MODEL,
related_name="assigned",
blank=True, null=True,
on_delete=models.CASCADE)
priority = models.IntegerField(choices=PRIORITY_CHOICES,
editable=True, default=3,
help_text='Please select a priority carefully. If unsure, leave it as "Normal".',
blank=True, null=True)
status = models.IntegerField(choices=STATUS_CHOICES,
editable=True,
default=1, blank=True, null=True)
closing_date = models.DateField(blank=True, null=True)
closing_notes = models.TextField(blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
upload = models.FileField(upload_to='uploads/%Y/%m/%d/', blank=True, null=True)
def get_absolute_url(self):
return reverse('tickets:ticket-detail', args=[str(self.id)])
def __str__(self):
return str(self.ticket_number)
class TicketFile(models.Model):
attachment = models.FileField(upload_to='attachments/%Y/%m/%d', blank=True, null=True)
ticket = models.ForeignKey(Ticket, editable=True, on_delete=models.CASCADE)
description = models.CharField(max_length=200, blank=True)
def get_image_filename(self):
if self.docfile:
return media_url + str(self.docfile)
def __str__(self):
return str(self.attachment)
# forms.py
class TicketFormIsStaff(ModelForm):
class Meta:
model = Ticket
exclude = ()
class TicketFileForm(forms.ModelForm):
attachment = forms.FileField(label='Attachment')
class Meta:
model = TicketFile
fields = ('attachment', )
TicketAttachmentFormset = modelformset_factory(Ticket,
form=TicketFileForm, extra=2)
# views.py
class TicketCreateView(CreateView):
model = Ticket
fields = ['ticket_number', 'client', 'title', 'description', 'status']
def get_context_data(self, **kwargs):
data = super(TicketCreateView, self).get_context_data(**kwargs)
if self.request.POST:
data['ticketfiles'] = TicketAttachmentFormset(self.request.POST)
print("data 1 ", data['ticketfiles'] )
else:
data['ticketfiles'] = TicketAttachmentFormset()
print("data 2 ", data)
return data
def form_valid(self, form):
context = self.get_context_data()
ticketfiles = context['ticketfiles']
with transaction.atomic():
self.object = form.save()
print("CONTEXT:::: ", ticketfiles)
if ticketfiles.is_valid():
ticketfiles.instance = self.object
ticketfiles.save()
return super(TicketCreateView, self).form_valid(form)
# ticket_form.html
<form action="" method="post" enctype="multipart/form-data">{% csrf_token %}
{{ form.as_p }}
<table class="table">
{{ ticketfiles.management_form }}
{% for form in ticketfiles.forms %}
{% if forloop.first %}
<thead>
<tr>
{% for field in form.visible_fields %}
<th>{{ field.label|capfirst }}</th>
{% endfor %}
</tr>
</thead>
{% endif %}
<tr class="{% cycle row1 row2 %} formset_row">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<input type="submit" value="Save" />
</form>
All the data related to the Ticket model uploads correctly, but the files related to the TicketFile model do not upload. If I try to upload the data in the admin interface then everything works perfectly. I assume this means that all the media settings are correct.
If I add the description field to the formset then that gets saved correctly. The file is not saved though.
I resolved the file upload problem by changing the code in TicketCreateView from:
data['ticketfiles'] = TicketAttachmentFormset(self.request.POST)
to:
data['ticketfiles'] = TicketAttachmentFormset(self.request.POST, self.request.FILES, instance=form.instance))

Handling one to one relationship between django models

I am having a one to one relationship between 2 models. While creating the second model, I want to pass the instance of the first model to the second one.
These 2 models are new tabs/features in our web application. I tried passing the instance through URL but didn't succeed. Maybe I am not following steps correctly.
Details about:
python version: Python 3.6.4 :: Anaconda, Inc.
django version: 2.0.2-3
Please find below the code:
1) models.py
class StudyConcept(models.Model):
requestor_name = models.CharField(max_length=240, blank=False, null=False)
project = models.CharField(max_length=240, blank=False, null=False)
date_of_request = models.DateField(blank=False, null=False)
brief_summary = models.CharField(max_length=4000, blank=False, null=False)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return str(self.id)
class Logistics(models.Model):
budget = models.CharField(max_length=255, blank=False, null=False)
business_technology = models.CharField(max_length=3, choices=CHOICES, blank=False, null=False)
vendor_or_contracts = models.CharField(max_length=3, choices=CHOICES, blank=False, null=False)
studyConcept = models.OneToOneField(StudyConcept, on_delete = models.CASCADE)
user = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return str(self.id)
def get_absolute_url(self):
return reverse('update_Logistics', kwargs={'pk': self.pk})
def get_deliverablesLogistics(self):
return ','.join([str(i) for i in self.deliverablesLogistics.all().values_list('id', flat=True)])
def get_paymentScheduleLogistics(self):
return ','.join([str(i) for i in self.paymentScheduleLogistics.all().values_list('id', flat=True)])
def get_commentsLogistics(self):
return ','.join([str(i) for i in self.commentsLogistics.all().values_list('id', flat=True)])
class DeliverablesLogistics(models.Model):
milestone_deliverable = models.CharField('MileStone/Deliverable', max_length=480, blank=False, null=False)
poa = models.CharField('POA', max_length=480, blank=False, null=False)
start_date = models.DateField(blank=False, null=False)
end_date = models.DateField(blank=False, null=False)
logistics = models.ForeignKey(Logistics, related_name='deliverablesLogistics', on_delete=models.CASCADE)
def __str__(self):
return str(self.id)
class PaymentScheduleLogistics(models.Model):
milestone_deliverable = models.CharField('MileStone/Deliverable', max_length=480, blank=False, null=False)
cost = models.DecimalField(max_digits=14, decimal_places=2, blank=False, null=False, default=0)
estimated_payment_date = models.DateField(blank=False, null=False)
logistics = models.ForeignKey(Logistics, related_name='paymentScheduleLogistics', on_delete=models.CASCADE)
def __str__(self):
return str(self.id)
class CommentsLogistics(models.Model):
comments = models.CharField('Comment', max_length=2000, blank=True, null=True)
commented_by = models.CharField(max_length=2000, blank=True, null=True)
logistics = models.ForeignKey(Logistics, related_name='commentsLogistics', on_delete=models.CASCADE)
def __str__(self):
return str(self.id)
views.py
def load_concepts(request):
currentUser = User.objects.get(id=request.user.id)
concepts = StudyConcept.objects.all().filter(user=request.user)
#concepts = get_object_or_404(StudyConcept)
return render(request, 'concept_dropdown_list_options.html',{
'concepts':concepts
})
class LogisticsFormsetCreate(CreateView):
model = Logistics
template_name = 'createLogistics.html'
form_class = LogisticsForm
success_url = reverse_lazy('create_Logistics')
def get_context_data(self, **kwargs):
data = super(LogisticsFormsetCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['deliverable'] = DeliverablesLogisticsFormset(self.request.POST, prefix='deliverables')
data['paymentSchedule'] = PaymentScheduleLogisticsFormset(self.request.POST, prefix='payments')
data['comment'] = CommentsLogisticsFormset(self.request.POST, prefix='comments')
#data['studyRequestConcept'] = self.request.POST.get('studyRequestConcept')
else:
data['deliverable'] = DeliverablesLogisticsFormset(prefix='deliverables')
data['paymentSchedule'] = PaymentScheduleLogisticsFormset(prefix='payments')
data['comment'] = CommentsLogisticsFormset(prefix='comments')
#data['studyRequestConcept'] = self.request.GET.get('studyRequestConcept')
return data
def form_valid(self, form):
context = self.get_context_data()
deliverable = context['deliverable']
paymentSchedule = context['paymentSchedule']
comment = context['comment']
with transaction.atomic():
if deliverable.is_valid() and paymentSchedule.is_valid() and comment.is_valid():
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object = form.save()
deliverable.instance = self.object
deliverable.save()
paymentSchedule.instance = self.object
paymentSchedule.save()
comment.instance = self.object
comment.save()
messages.success(self.request, Logistics.__name__ +' Form ID: '+ str(self.object.id) + ' was submitted successfully')
return super(LogisticsFormsetCreate, self).form_valid(form)
else:
return self.render_to_response(self.get_context_data(form=form))
Template
{% extends "header.html" %}
{% load widget_tweaks %}
{% block content %}
{% csrf_token %}
{% include 'xdsoft_stylesheets.html' %}
{% include 'messages.html' %}
<div class="container" align="center">
<h1 class="display-5">Logistics</h1>
</div>
<br/>
<div class="table-responsive">
<table class="table table-striped table-bordered" id="example" data-toggle="table"
data-filter-control="true" data-show-export="true"
data-click-to-select="true" data-toolbar="#toolbar" data-escape>
<thead>
<tr>
<th></th>
<th class="text-center" data-field="id" data-filter-control="input">ID</th>
<th class="text-center" data-field="project" data-filter-control="input">Project</th>
<th class="text-center" data-field="date_of_request" data-filter-control="input">Date</th>
<th class="text-center" data-field="brief_summary" data-filter-control="input">Summary</th>
<th class="text-center" data-field="scientific_question" data-filter-control="input">Question</th>
</tr>
</thead>
<tbody>
{%for studyRequestConcept in concepts %}
<tr>
<td style="width:200px">
<a class=" btn btn-primary js-create-logistics" data-toggle="modal" data-target="#modal" href="{% url 'create_Logistics' %}">
<span class="glyphicon glyphicon-plus"></span>
New Logistics
</a>
</td>
<td class="text-left">{{studyRequestConcept.id}}</td>
<td class="text-left">{{studyRequestConcept.project}}</td>
<td class="text-left">{{studyRequestConcept.date_of_request}}</td>
<td class="text-left">{{studyRequestConcept.brief_summary}}</td>
<td class="text-left">{{studyRequestConcept.scientific_question}}</td>
</tr>
{% endfor%}
</tbody>
</table>
</div>
{% comment %}The modal container{% endcomment %}
<div class="modal" id="modal" data-backdrop="false"></div>
<script>
$(function () {
$('.js-create-logistics').click( function () {
var btn = $(this)
$.ajax({
url: btn.attr("href"),
context: document.body
}).done(function(response) {
$("#modal").html(response);
});
});
});
</script>
{% endblock %}
I have a view/template where I list all the Study Concepts and every row has a create new Logistics button next to it. After clicking create new logistics button, the modal/view will open that will allow you to create a new Logistics. I want to pass the instance of the object study concept when I click the button.
Also, CreateLogistics is a class-based view designed using "from django.views.generic import CreateView"
I will be more than happy to provide any further code or information needed. Thanks in advance for all the support and help.
Regards,
Amey Kelekar
You haven't shown your URLs, but you need to accept the ID in the URL for the CreateView and use it in form_valid. For example:
path('/create_logistics/<int:id>/', LogisticsFormsetCreate.as_view(), name="create_Logistics"),
So in the template you would do:
<a class="btn btn-primary js-create-logistics" data-toggle="modal" data-target="#modal" href="{% url 'create_Logistics' id=studyRequestConcept.id %}">
and in the view:
def form_valid(self, form):
...
self.object = form.save(commit=False)
self.object.user = self.request.user
self.object.studyConcept_id = self.kwargs['id']
self.object.save()
...