How do I sort products on django? [closed] - django

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed yesterday.
Improve this question
Can you show how to sort the goods in the online store? So that you can click on the button and the sorting has changed, for example: price,-price. And to views.py was in class, not in def.
views.py
class SectionView(View):
def get(self, request, *args, **kwargs):
sort_form = request.GET.getlist('sort')
products = Product.objects.filter(available=True)
if sort_form.is_valid():
needed_sort = sort_form.cleaned_data.get("sort_form")
if needed_sort == "ДТ":
products = products.order_by(
"created") # или updated в зависимости от того, что ты вкладываешь в понятие по дате
elif needed_sort == "ДЕД":
products = products.order_by("price")
elif needed_sort == "ДОД":
products = products.order_by("-price")
return render(
request=request,
template_name='main/index.html',
context={
'products':products,
}
)
form.py
class SortForm(forms.Form):
sort_form = forms.TypedChoiceField(label='Сортировать:', choices=[('ПУ', 'По умолчанию'), ('ДТ', 'По дате'), ('ДЕД', 'От дешевых к дорогим'), ('ДОД', 'От дорогих к дешевым')])

You can try something like that:
def get(self, request)
allowed_orderings = ('price', '-price', ...) # define orderings you want
ordering = request.GET.get('ordering', '-price') # get ordering from url params
ordering = ordering if ordering in allowed_orderings else '-price' # check ordering is allowed, if not - set the default
your_model = Model.objects.filter(...).order_by(ordering) # filter queryset with your params and then order by defined ordering
... # and then return your models as you want to
For this code you need to provide ordering in your url params (like /shop/goods/?ordering=-price)

Related

How to save subform in parent form django ModelForm? [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 9 days ago.
Improve this question
Dears, please check this below scenario, I am trying to submit main form with sub form thats is a foreign key to the main form.
class ServiceProviderTransaction(BaseModel):
serial_number = models.IntegerField(unique=True)
service_provider_bill = models.OneToOneField(ServiceProviderBill, on_delete=models.CASCADE)
with the following forms.py
class ServiceProviderTransactionForm(forms.ModelForm):
class Meta:
model = ServiceProviderTransaction
fields = "__all__"
I have subform which is Bill inside Transaction form, upon submission of transaction form, I want my bill form to be saved as well with single submit, please advice
class ServiceProviderFormSubmissionView(View):
def get(self, request):
current_provider = ServiceProvider.objects.get(pk=1)
form = ServiceProviderTransactionForm(
initial={
"service_provider": current_provider,
}
)
context = {
"form": form,
"bill": ServiceProviderBillForm(),
}
return render(request, "provider_moves/sp-form.html", context)
def post(self, request):
transaction = ServiceProviderTransactionForm(request.POST)
bill = ServiceProviderBillForm(request.POST)
ServiceProviderBillForm["service_provider_bill"] = bill
# transaction["service_provider_bill"] = ServiceProviderBillForm(request.POST)
if transaction.is_valid():
# bill = bill.save()
transaction = transaction.save()
return redirect(reverse("spt-view"))
#else
context = {
"form": transaction,
"bill": bill,
}
return render(request, "provider_moves/sp-form.html", context)

How to make Django API with registration and login [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
I am trying to make an API in Django that returns employee info as JSON only when a user is logged in.
My question is, how can I make a user registration and log in option without the use of html and forms?
Is there a way for me to create a registration option that takes in input sent by a client via JSON. For example, a POST request to localhost:example/register/ which contained :
{"username" : "examle", "email" : "example", "password" : "example", "password2" : "example"}?
How would this be implemented in views.py?
Any help would be appreciated.
Thank you.
Yes, there is a way, and Django Rest Framework includes almost everything you need.
To answer your first question about being logged in, you would add a permissions class to only allow access to someone who is authenticated.
To answer your API question for registration, you'll want to create an API endpoint in whichever URL file is relevant; I put my in my Users app:
urlpatterns = [
path('register/', register_user_view, name="register"),
...
]
You'll want to create a view and serializer to ingest the JSON you're sending in:
#api_view(['POST',])
def register_user_view(request):
if request.method == 'POST':
serializer = RegistrationSerializer(data=request.data)
data = {}
if serializer.is_valid():
account = serializer.save()
data['response'] = "successfully registered a new user."
data['email'] = account.email
data['response'] = account.username
else:
data = serializer.errors
# data = serializer.data
return Response(data)
Serializer:
class RegistrationSerializer(serializers.ModelSerializer):
password2 = serializers.CharField(style={'input_type': 'password'}, write_only=True)
class Meta:
model = CustomUser
fields = ['email', 'username', 'password', 'password2']
extra_kwargs = {
'password': {'write_only': True}
}
def save(self):
account = CustomUser(
email=self.validated_data['email'],
username=self.validated_data['username'],
)
password = self.validated_data['password']
password2 = self.validated_data['password2']
if password != password2:
raise serializers.ValidationError({'password': 'Passwords do not match!'})
account.set_password(password)
account.save()
return account

Django ListView - Form to filter and sort

My Goal
A site that list all my Updates (model) in a table
Dont display all models at once (pagination - maybe 10 per page)
Filter and sort the list
My thoughts
I can use ListView to get a set of all my Updates
Use paginate_by = 10
Use a form to set order_by or filter in my QuerySet
My Problem
I am not sure how to add an form to modify my QuerySet with filter and sortings. My Idea was to modify the Query in get_queryset with additional filter and order_by.
My View
class MyView(ListView):
model = Update
template_name = "updates/update.html"
paginate_by = 10
def get_queryset(self):
return Update.objects.filter(
~Q(state=Update.STATE_REJECTED),
~Q(state=Update.STATE_CANCELED),
~Q(state=Update.STATE_FINISHED),
).order_by(
'planned_release_date'
)
My Idea
Something like this. I know it's not working like this ... just to illustrate
class MyView(ListView):
model = Update
template_name = "updates/update.html"
paginate_by = 10
def post(self, request, *args, **kwargs):
new_context = Update.objects.filter(
request.POST.get("filter"),
).order_by(
request.POST.get("sorting"),
)
def get_queryset(self):
return Update.objects.filter(
~Q(state=Update.STATE_REJECTED),
~Q(state=Update.STATE_CANCELED),
~Q(state=Update.STATE_FINISHED),
).order_by(
'planned_release_date'
)
You don't need post. Pass the filter value and order_by in the url for example:
.../update/list/?filter=filter-val&orderby=order-val
and get the filter and orderby in the get_queryset like:
class MyView(ListView):
model = Update
template_name = "updates/update.html"
paginate_by = 10
def get_queryset(self):
filter_val = self.request.GET.get('filter', 'give-default-value')
order = self.request.GET.get('orderby', 'give-default-value')
new_context = Update.objects.filter(
state=filter_val,
).order_by(order)
return new_context
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['filter'] = self.request.GET.get('filter', 'give-default-value')
context['orderby'] = self.request.GET.get('orderby', 'give-default-value')
return context
Make sure you give proper default value to filter and orderby
Example form (you can modify this to your need):
<form method="get" action="{% url 'update-list' %}">
<p>Filter: <input type="text" value={{filter}} name="filter"/></p>
<p>order_by: <input type="text" value={{orderby}} name="orderby"/></p>
<p><input type="submit" name="submit" value="submit"/></p>
</form>
I am wondering why nobody mentioned here this cool library: django-filter https://github.com/carltongibson/django-filter
you can define your logic for filtering very clean and get fast working forms etc.
demo here: https://stackoverflow.com/a/46492378/953553
I posted this elsewhere but I think this adds to the selected answer.
I think you would be better off doing this via get_context_data. Manually create your HTML form and use GET to retrieve this data. An example from something I wrote is below. When you submit the form, you can use the get data to pass back via the context data. This example isn't tailored to your request, but it should help other users.
def get_context_data(self, **kwargs):
context = super(Search, self).get_context_data(**kwargs)
filter_set = Gauges.objects.all()
if self.request.GET.get('gauge_id'):
gauge_id = self.request.GET.get('gauge_id')
filter_set = filter_set.filter(gauge_id=gauge_id)
if self.request.GET.get('type'):
type = self.request.GET.get('type')
filter_set = filter_set.filter(type=type)
if self.request.GET.get('location'):
location = self.request.GET.get('location')
filter_set = filter_set.filter(location=location)
if self.request.GET.get('calibrator'):
calibrator = self.request.GET.get('calibrator')
filter_set = filter_set.filter(calibrator=calibrator)
if self.request.GET.get('next_cal_date'):
next_cal_date = self.request.GET.get('next_cal_date')
filter_set = filter_set.filter(next_cal_date__lte=next_cal_date)
context['gauges'] = filter_set
context['title'] = "Gauges "
context['types'] = Gauge_Types.objects.all()
context['locations'] = Locations.objects.all()
context['calibrators'] = Calibrator.objects.all()
# And so on for more models
return context
This is how we do it, that way you get validation/type conversion as well:
class UnitList(PermissionRequiredMixin, ListView):
""" Class based view to show a list of all buildings for a specific user """
model = Unit
ordering = ['building', 'unit']
paginate_by = 100
# Access
permission_required = ['core.manager_perm']
raise_exception = True # If true, give access denied message rather than redirecting to login
def get_queryset(self):
try:
units = self.model.objects.filter(building__company=self.request.user.profile.company)
except Profile.DoesNotExist:
units = self.model.objects.none()
form = UnitSearchForm(self.request.GET)
if form.is_valid():
filters = {}
address = form.cleaned_data['address']
neighborhood = form.cleaned_data['neighborhood']
beds = form.cleaned_data['beds']
amenity = form.cleaned_data['amenity']
if address:
filters['building__street_index__istartswith'] = compute_street_address_index(address)
if neighborhood:
filters['building__neighborhood__icontains'] = neighborhood
if beds:
filters['beds'] = beds
if amenity:
filters['unit_amenities__name__iexact'] = amenity
units = units.filter(**filters)
return units.select_related('building').order_by(*self.ordering)
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['form'] = UnitSearchForm(self.request.GET)
return context

Dynamic View not displayed

I am trying to take a quiz and if you answer a question wrong you see that question get added to a list of wrong questions.
Questions can be used in multiple quizes so I can't hard code ask question_1 then question_2 etc.
When I load the page I see the first question but then if I submit or refresh I see the right.html template. It doesn't ask all of the questions.
It should ask all of the questions before rendering the right.html page
for question in quiz.questions.all():
if question not in asked_questions:
asked_questions.append(question)
return answer_question(request, quiz_id, question.id, module_list)
models.py
class Choice(models.Model):
choice = models.CharField(max_length=64)
def __unicode__(self):
return self.choice
#create a multiple choice quiz to start
class Question(models.Model):
question = models.CharField(max_length=64)
answer = models.CharField(max_length=64)
choices = models.ManyToManyField(Choice)
module = models.CharField(max_length=64)
def __unicode__(self):
return self.question
class Quiz(models.Model):
name = models.CharField(max_length=64)
questions = models.ManyToManyField(Question)
def __unicode__(self):
return self.name
views.py
asked_questions = []
module_list = []
module_list.append('test')
def take_quiz(request, quiz_id):
quiz = Quiz.objects.get(id=str(quiz_id))
for question in quiz.questions.all():
if question not in asked_questions:
asked_questions.append(question)
return answer_question(request, quiz_id, question.id, module_list)
#quiz is over
return render (request, 'right.html')
def answer_question(request, quiz_id, question_id, module_list):
question = Question.objects.get(id=question_id)
#module_list = []
if request.method == 'POST':
form = QuestionForm(request.POST)
choices = [(i, i) for i in question.choices.all()]
form.fields['selection'].choices = choices
if form.is_valid():
if form.cleaned_data['selection'] != str(question.answer):
module_list.append(question.module)
return take_quiz(request, quiz_id)
else:
form = QuestionForm()
choices = [(i, i) for i in question.choices.all()]
form.fields['selection'].choices = choices
return render(request, 'answer_question.html', {'question':question, 'form':form, 'module_list':module_list})
If you plan on hosting this and making it available on the web, you should really start over. You shouldn't use lists like that to store stuff, you should use sessions or the cache instead. Also, you would be better off using a FormWizard for the quiz since it is designed to do exactly what you are trying to do: handle multiple forms and process them.
Maybe you should try
return HttpResponseRedirect(reverse('answer_question', args=[quiz_id, question_id, module_list]))
to redirect your view to another view.
p.s.: reverse should contain the name of your 'answer_question' view

how to overide in forms queryset none() attribute and somehow allow to save the field?

I have models.py
class Visit(Model):
reference_visit = models.ForeignKey('self',
help_text="Visit needs a refrence to Prior Visits",
null=True, blank=True)
show_prior_responses = models.BooleanField(default=False,
help_text="Show PriorResponses")
# has many field but i am making it short.
def __unicode__(self):
result = """Visit id:%s pt:%s""" % (self.id, self.patient.id)
return result
forms.py
class VisitSetupForm(Form):
list_visit_ids = ModelChoiceField(
queryset=Visit.objects.none(),
empty_label='Select Revisit ID',required=False)
show_prior_visit = ModelChoiceField(
queryset=User.objects.all(),
empty_label="Select User for Revisit",required = False)
has many but question is on list_visit_ids.
views.py
def setup(request):
"""
Allow an Admin user the ability to setup a patient & visit all at once.
"""
if request.user.is_superuser:
form_class = AdminVisitSetupForm
all_topics = True
else:
form_class = VisitSetupForm
all_topics = False
f = form_class()
# Get a list of topics for each report.
report_topics = {}
for r in Interview.objects.all():
report_topics[r.id] = [t['ad'] for t in r.topics.values('ad')]
data = {
'superuser':request.user.is_superuser,
'report_topics':simplejson.dumps(report_topics)
}
try:
request.user.reviewer
data['reviewer'] = True
except:
pass
if request.method == "POST":
f = form_class(request.POST)
if f.is_valid():
# Create the patient, generate a password, and send them on their way.
cd = f.cleaned_data
patient = None
if cd['revisit']:
# Check for an existing user first.
try:
patient = Patient.objects.get(username=cd['username'])
except Patient.DoesNotExist, e:
data['form'] = f
data['msg'] = 'There is no user with this username.'
return render_to_response('visit/setup.html', data, context_instance=RequestContext(request))
admin_user = get_user(request)
organization = None
if admin_user:
organization = admin_user.organization
if patient and not request.user.is_superuser:
# Make sure the patient they've selected is one of their own.
if patient.organization != organization:
return HttpResponseForbidden('You are not allowed to see this page.')
if not patient:
password = generate_password()
user = User.objects.create_user(cd['username'], cd['contact_email'], password)
user.first_name = cd['first_name']
user.last_name = cd['last_name']
user.save()
patient = Patient(
user=user,
username=user.username,
contact_phone=cd['contact_phone'],
date_of_birth=cd['date_of_birth'],
email=user.email,
first_name=user.first_name,
gender=cd['gender'],
last_name=user.last_name,
maiden_name=cd['maiden_name'],
organization=organization,
patient_type=cd['patient_type'],
security_answer=cd['security_answer'],
security_question=cd['security_question'],
)
patient.save()
# Send them an email.
t = loader.get_template('www/new_account.txt')
c = Context({
'password':'%s-%s-%s' % (password[:3], password[3:5], password[5:]),
'patient':patient
})
msg = t.render(c)
try:
send_mail(
'A request by your physician to do an online medical history before your appointment.',
msg,
'support#careprep.com',
[user.email]
)
except Exception, e:
log.error('Could not send email for new account %s because: [%s]' % (user.username, e))
request.session['password'] = password
# Create the Visit, too.
interview = cd['interview']
list_visit_ids = cd['list_visit_ids']
print list_visit_ids
visit = Visit(
reference_visit = cd['list_visit_ids'],
show_prior_responses = cd['show_prior_responses'],
patient=patient
)
if request.user.is_superuser:
topics = cd['topics']
else:
topics = set(list(interview.topics.all()) + list(cd['topics']))
reviewer_mode = cd.get('reviewer_mode') or patient.patient_type == 'Reviewer'
url, visit = initialize_visit(
request,
patient=patient,
starting_section=interview.starting_section,
visit_title='%s %s' % (patient, interview.title),
topics=topics,
reviewer_mode=reviewer_mode,
chief_complaint=cd['chief_complaint'],
location=cd['interview_site'],
reference_visit = cd['list_visit_ids'],
show_prior_responses = cd['show_prior_responses'],
)
next_url = "/visit/confirmation/%s/%s/?next=%s" % (patient.user.id, interview.id, url)
else:
v = Visit.objects.get(pk=request.POST['list_visit_ids'])
print v
return HttpResponseRedirect(next_url)
# all the fields that are not given pls ignore.
The template is fine.
Now watch forms.py when i do list_visit_ids = ModelChoiceField(queryset=Visit.objects.all(), empty_label='Select Revisit ID',required=False) It works perfectly fine on my local machine.But on my server it has around 6000 visit objects so this page hangs or i should say keep on loading.
So initially i changed it to list_visit_ids = ModelChoiceField(queryset=Visit.objects.none(), empty_label='Select Revisit ID',required=False)
Now i know that by this the form becomes invalid and should go to the else part Now my question how do i make reference_visit=cd['list_visit_ids'] in else (form is invalid)
case save().How do i override the none() attribute.
Thanks in advance i will really appreciate.
If your goal is to save your html page load by removing the 6000 choices (which I've done too: 10000+ <option> fields wrapped by misc html will absolutely choke a page), you shouldn't be using a ChoiceField at all. By setting queryset=Visit.objects.none() you're allowing zero choices and nothing passed in will validate.
You either show 6000 select item drop downs, radio boxes, etc., or find a way to /not/ have a giant select drop down (such as a hidden input or charfield), not fake around a ModelChoiceField who's main purpose is to populate that select drop down and validate.
In short: don't use a ModelChoiceField if you're not going to be using the html choices generated by it. Use something else and do the validation / model pulling yourself via the clean_FOO methods.
class MyForm(forms.Form):
my_input = forms.CharField()
def clean_my_input(self):
input = self.cleaned_data.get('my_input')
try:
return MyModel.objects.get(pk=input) # add a filter here if you want
# (whatever filters you were using in the queryset argument)
except MyModel.DoesNotExist:
raise forms.ValidationError("Doesn't exist / is invalid")
return input