Python Django: Insert dynamic form records to db - python

It is a Medical Lab Software solution. A user makes lab investigation request that is unique to a patient’s encounter, I.e., a patient can have 1 or more request per encounter. This uniqueness is denoted by the encounter_id.
The challenge now is the ability to send the results back to the requester.
I am able to display all requests per encounter_id on a template but unable to return the result since each result is tied to a particular investigation. This is largely because I have a limited knowledge on JS. My current approach can only submit one record, usually the last record
Here’s the URL that displays the result template: https://smart-care.herokuapp.com/labs/lab_results/1/
Here’s the django template:
<div class="container">
<form id="myForm" method="POST" action="">{% csrf_token %}
{% for request in lab_request %}
{{ request.test }}: <input type="text" class="result" id="{{ request.test.id }}" name="test_id"> <br>
{% endfor %}
<br><br>
<button>Send Result</button>
</form>
</div>
# Here’s my view:
def lab_results_view(request, enc_id):
lab_request = LabRequest.objects.filter(encounter_id=enc_id, done=False, decline=False)
if request.POST.get('test_id'):
for req_id in request.POST:
results = request.POST.get(req_id, False)
print("results = ",results)
template = "labs/lab_results.html"
context = {"lab_request":lab_request}
return render(request, template, context)
The models:
class LabRequest(models.Model):
encounter = models.ForeignKey(PatientEncounter, on_delete=models.CASCADE, blank=True, null=True)
patient = models.ForeignKey(Patient, on_delete=models.CASCADE, blank=True, null=True)
test = models.ForeignKey(LabTest, on_delete=models.CASCADE)
accepted = models.BooleanField(default=False)
decline = models.BooleanField(default=False)
done = models.BooleanField(default=False)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
date_created = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __str__(self):
return str(self.patient.id)
class LabResult(models.Model):
lab_request = models.ForeignKey(LabRequest, on_delete=models.CASCADE, blank=True, null=True)
result = models.CharField(max_length=225)
created_by = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True)
date_created = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now_add=False, auto_now=True)
def __str__(self):
return str(self.result)
Thanks as I anticipate your kind response.

The problem that Django handles form elements based on name attribute and not the id attribute so this means you need to change name per request and on the view side you loop on all keys in request.POST like
for req_id in request.POST:
result = Request.POST[req_id]
Edit: for the template
<div class="container">
<form id="myForm" method="POST" action="">{% csrf_token %}
{% for request in lab_request %}
{{ request.test }}: <input type="text" class="result" id="{{ request.test.id }}" name="{{ request.test.id }}">
<br> {% endfor %} <br><br>
<button>Send Result</button>
</form> </div>
Edit2: The view shall be like this
def lab_results_view(request, enc_id):
lab_request = LabRequest.objects.filter(encounter_id=enc_id, done=False, decline=False)
if request.method == "POST":
for req_id in request.POST:
results = request.POST.get(req_id)
print("results = ",results)
template = "labs/lab_results.html"
context = {"lab_request":lab_request}
return render(request, template, context)

Related

(Best practice) Handling multiple simple forms relating to one object in one view

I have a working solution but I would like to know if this is the best way to do it.
I have three models: Meal, Indgredient and IngredientWithQuantity (IwQ). Meal can have multiple IwQ and Ingredient can have multiple IwQ.
I created a simple view where you can create Meal (one form), add IwQ (another form) and if needed go to another view to create new Ingredient (yet another form) and then come back to this view where you still will edit the previous Meal.
The only problem is keeping the information about the meal that is being created.
I am doing this through html hidden input field where I store an information about meal_id.
I am simply not sure if this is the best way to exchange this information, because at times it seems a little off.
Could some take a look and tell me if maybe this could be achieved in a more effective manner?
Thanks,
models.py
class Ingredient(models.Model):
name = models.CharField(max_length=200, null=True)
#brand
#WW
def __str__(self):
return self.name
class Meal(models.Model):
EVALUATION = (
('Perfect', 'Perfect'),
('Average', 'Average'),
('Bad', 'Bad')
)
name = models.CharField(max_length=200, null=True)
time_eaten = models.DateTimeField(auto_now=False, auto_now_add=False, default=timezone.now, null=True)
# time_of_day = jaki posiłek?
bolus_n = models.DecimalField(max_digits=2, decimal_places=1, default=0, null=True)
bolus_s = models.DecimalField(max_digits=2, decimal_places=1, default=0, null=True, blank=True)
bolus_s_duration = models.DecimalField(max_digits=3, decimal_places=2, default=0, null=True, blank=True)
interval = models.IntegerField(null=True, default=0)
prev_bolus_n = models.DecimalField(max_digits=2, decimal_places=1, default=0, null=True, blank=True)
prev_bolus_s = models.DecimalField(max_digits=2, decimal_places=1, default=0, null=True, blank=True)
prev_bolus_s_duration = models.DecimalField(max_digits=3, decimal_places=2, default=0, null=True, blank=True)
prev_bolus_time = models.DateTimeField(auto_now=False, auto_now_add=False, default=timezone.now, null=True)
evalution = models.CharField(max_length=200, null=True, choices=EVALUATION)
notes = models.CharField(max_length=1500, null=True)
#recipe = models.ForeignKey(Recipe, null=True, on_delete=models.SET_NULL)
class IngredientWithQuantity(models.Model):
UNITS = (
('g', 'g'),
('spoon', 'spoon'),
('cup', 'cup'),
)
ingredient = models.ForeignKey(Ingredient, null=True, on_delete=models.SET_NULL)
quantity = models.IntegerField(null=True)
unit = models.CharField(max_length=10, choices=UNITS, null=True, blank=True)
meal = models.ForeignKey(Meal, null=True, on_delete=models.CASCADE)
#recipe = models.ForeignKey(Recipe, null=True, on_delete=models.CASCADE)
def __str__(self):
caption = str(self.quantity) + ' ' + str(self.unit) + ' ' + self.ingredient.name
return caption
views.py:
def createMeal2(request):
print(request.POST)
if 'meal_id' not in request.POST or request.POST['meal_id'] == '':
meal = Meal()
meal.save()
meal_id = meal.id
else:
meal_id = request.POST['meal_id']
meal = Meal.objects.get(id=meal_id)
if 'meal_form' in request.POST:
meal_form = MealForm(request.POST, instance=meal)
if meal_form.is_valid():
meal_form.save()
return redirect('/boot/')
iwq = IngredientWithQuantity(meal = meal)
meal_form = MealForm(instance=meal)
if 'ingredient_form' in request.POST:
ing_form = IngredientForm(request.POST)
if ing_form.is_valid():
ing_form.save()
iwq.ingredient = Ingredient.objects.get(name=request.POST['name'])
if request.method == 'POST' and 'iwq_form' in request.POST:
iwq_form = IwQForm(request.POST, instance=iwq)
if iwq_form.is_valid():
iwq_form.save()
else:
iwq_form = IwQForm(instance=iwq)
context = {'iwq_form':iwq_form, 'meal':meal, 'meal_id':meal_id, 'meal_form':meal_form}
return render(request, 'meals/create_meal2.html', context)
template:
{% extends 'meals/main.html' %}
{% load static %}
{% block content %}
<hr>
<h2>Meal: {{meal.name}}
<hr>
<h2>Ingredients2</h2>
<form action="/create_ingredient/" method="POST">
{% csrf_token %}
<input type = "hidden" name="meal_id" value={{meal_id}}>
<input type ="submit" class="btn btn-primary" name="add_ingredient" value="Dodaj składnik">
</form>
<br>
<form method="POST">
{% csrf_token %}
<table border="1">
{% for field in iwq_form.visible_fields %}
<tr>
<th>{{ field.label_tag }}</th>
<td>
{{ field.errors }}
{{ field }}
{{ field.help_text }}
</td>
<td>
</td>
</tr>
</tr>
{% endfor %}
</table>
<input type="submit" value = "Dodaj składnik" name="iwq_form">
<input type="hidden" name="meal_id" value={{meal.id}}>
</form>
<h2>Ingredients</h2>
{% for i in meal.ingredientwithquantity_set.all %}
{{i}} <br>
{% endfor %}
<hr>
<form action="." method="POST">
{% csrf_token %}
{{meal_form.as_p}}
<input type="hidden" name="meal_id" value={{meal.id}}>
<input type ="submit" class="btn btn-primary" name="meal_form" value="Zapisz posiłek">
{% endblock %}
For this task I think you can use formtools. It divides the forms into multiple pages and user get to fill them with step by step method. You can skip certain forms depending on answers the user gave beforehand etc. It is very useful and at the end you get the entire data for all the forms. You can also get data for a specific step at a certain point. Here is the link to an article: https://swapps.com/blog/how-to-do-a-wizard-form/
Also here is the documentation link: https://django-formtools.readthedocs.io/en/latest/wizard.html

Why Am I getting "None" when using ForeignKey on integer value from another model that I know that have integer number?

I wanted to ask you for your help. I have two models. In the first one I wanted to reference foriegn key from the second one to be able to print votescore that I store there.
My models.py :
class Question(models.Model):
question = models.CharField(max_length=300)
answered = models.BooleanField(default=False)
created = models.DateTimeField(auto_now_add=True)
datecompleted = models.DateTimeField(null=True, blank=True)
user = models.ForeignKey(User, on_delete=models.CASCADE)
votesscore = models.ForeignKey('VoteQuestion', on_delete=models.CASCADE, null=True, blank=True, related_name='question_votesscore')
def __str__(self):
return self.question
class VoteQuestion(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
question = models.ForeignKey(Question, on_delete=models.CASCADE, blank=False, null=True)
votesubmitted = models.DateTimeField(null=True, blank=True)
votesscore = models.IntegerField(default='0')
amountofvotes = models.IntegerField(default='0')
def __str__(self):
return self.votesscore
class Meta:
unique_together = ['user', 'question', 'votesscore']
Next in my views.py:
def home(request):
allquestionswithanswers = Question.objects.filter(datecompleted__isnull=False)
allquestionswithoutanswers = Question.objects.filter(datecompleted__isnull=True)
return render(request, 'main/home.html', {'allquestionswithanswers': allquestionswithanswers, 'allquestionswithoutanswers': allquestionswithoutanswers})
And in my home.html I am calling it like this:
{% for question in allquestionswithanswers %}
<li>
{{ question }} Score: {{ question.votesscore }} {{ question.user }}
<br><br>
<form class='my-ajax-form' method='POST' action='' data-url="{% url 'questionvoteup' question.id %}" >
{% csrf_token %}
<button type='submit'>UP</button>
</form>
{% for answer in question.answer_set.all %}
{{ answer }}<br>
{% endfor %}
</li>
{% endfor %}
And when I try to print {{ question.votesscore }} I get value "None". Yet I am sure that in database it is an integer value. Could you please point me in right direction with this ?
Thanks and Cheers

How to check existence of a record in a model from current authenticated user in Django template?

Code below checks whether a user added a product in cart or not. If it is added to cart by this current user it should show remove from cart button else it should show a simple form to add product in cart.
{% for ordereditem in item.ordereditems_set.all %}
{% if ordereditem.quantity > 0 and ordereditem.user.username == user.username %}
Remove from cart
{% elif not ordereditem %} # here!
<!-- else if there is no record of 'ordereditem' from current user show this form to add it to cart-->
<form class="d-flex justify-content-left" method="POST" action="{{ item.get_add_to_cart_url }}">
{% csrf_token %}
<input type="number" name="number" value="1">
<input type="submit" value="Add to cart">
</form>
{% endif %}
{% endfor %}
the problem lies here {% elif not ordereditem %} it seems like my current if statement doesn't meet the condition I expect. I tried using {% else %} but it still shows the form even after adding product to cart.
This is how models look like:
class Item(models.Model):
title = models.CharField(max_length=100)
price = models.FloatField()
discount_price = models.DecimalField(max_digits=5,
decimal_places=2, verbose_name='Discount', null=True, blank=True)
image = models.ImageField(upload_to='products/%Y/%m/%d/')
image_cover = models.ImageField(upload_to='products/%Y/%m/%d/')
description = models.TextField()
slug = models.SlugField(max_length=150, blank=True, null=True)
category = models.CharField(max_length=15, choices=CATEGORY_CHOICE)
label = models.CharField(max_length=10, choices=LABEL_CHOICE)
associated_items = models.ManyToManyField("self", blank=True, null=True)
class OrderedItems(models.Model):
item = models.ForeignKey(Item, on_delete=models.CASCADE)
quantity = models.IntegerField(default=1)
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
ordered = models.BooleanField(default=False)
class Order(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
items = models.ManyToManyField(OrderedItems)
start_date = models.DateTimeField(auto_now_add=True)
ordered_date = models.DateTimeField()
ordered = models.BooleanField(default=False)
Here is the github link. Thank You
You need {% empty %} here:
{% for ordereditem in item.ordereditems_set.all %}
...
{% empty %}
No items
{% endfor %}

django, does not display the avatar in the comments

I extended standart django user model by one-to-one field. Made news block, and added comments there. In comments i cant display user avatar from UserProfile model, cause dont understand how correctly ask database for it D;. Here my code:
main/models.py
from django.db import models
from django.utils import timezone
from django.contrib import auth
from django.contrib.auth.forms import User
from django.shortcuts import render, redirect
from profiles.models import UserProfile
# Create your models here.
class News(models.Model):
news_title = models.CharField(max_length=250)
news_body = models.TextField(max_length=2000, blank=True)
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
image = models.FileField()
published_date = models.DateTimeField(blank=True, null=True)
def publish(self, request):
self.published_date = timezone.now()
self.save()
return redirect('index')
def __str__(self):
return self.news_title
class Comment(models.Model):
news = models.ForeignKey('main.News', related_name='comments',
on_delete=models.CASCADE)
author = models.CharField(max_length=200)
text = models.TextField()
created_date = models.DateTimeField(default=timezone.now)
approved_comment = models.BooleanField(default=False)
def approve(self):
self.approved_comment = True
self.save()
def __str__(self):
return self.text
profiles/models.py
class UserProfile(models.Model):
JEW_CHOICE = (
('Да', 'Да'),
('Нет', 'Нет'),
)
MF_CHOICE = (
('М', 'М'),
('Ж', 'Ж')
)
user = models.OneToOneField(User, on_delete=models.CASCADE)
country = models.CharField(max_length=100, default='', blank=True)
city = models.CharField(max_length=100, default='', blank=True)
description = models.CharField(max_length=500, default='', blank=True)
website = models.URLField(default='', blank=True)
avatar = models.ImageField(default='', blank=True)
gender = models.CharField(max_length=100, choices = MF_CHOICE, default = 'М', blank=True)
jew = models.CharField(max_length=100, choices = JEW_CHOICE, default = 'Да', blank=True)
def __str__(self):
return self.user.username
#receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.get_or_create(user=instance)
#receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
instance.userprofile.save()
#property
def avatar_url(self):
if self.avatar and hasattr(self.avatar, 'url'):
return self.avatar.url
main/views.py (meme_detail is the view, where should be comments with user info)
def meme_detail(request, pk):
news = get_object_or_404(News, pk=pk)
if request.method == "POST":
form = CommentForm(request.POST)
if form.is_valid():
comment = form.save(commit=False)
comment.author = request.user
comment.news = news
comment.save()
return redirect('main:meme_detail', pk=news.pk)
else:
form = CommentForm()
return render(request, 'main/meme_detail.html', {'news': news, 'form': form,})
meme_detail.html (news template with comments)
{% extends 'main/base.html' %}
{% block body %}
<h2>{{news.news_title}}</h2>
<img src='{{news.image.url}}' name='image' width='500px;'><br>
{{news.news_body}} <br><br>
<div class="row">
<div class="col">
<b>{{news.author}}</b>
</div>
<div class="col">
<i>{{news.published_date}}</i>
</div>
</div>
<div class="underline"></div>
<h3>Комментарии:</h3><br>
{% for comment in news.comments.all %}
<div class="row">
<div class="col-"><img src="{{ userprofile.avatar.url }}" alt="user-avatar" width="100px" height="100px"></div>
<div class="col">{{ comment.text }}</div>
</div>
<div class="row">
<div class="col"><strong>{{ comment.author }}</strong></div>
<div class="col">{{ comment.created_date}}</div>
</div>
<div class="underline"></div>
<br>
{% empty %}
<p>Пока ещё нет комментариев :(</p>
{% endfor %}
{% if request.user.is_authenticated %}
<div class="row">
<form method="POST">
{% csrf_token %}
{{form.text}}<br><br>
<a class="btn btn-success" href="{% url 'main:meme_detail' pk=news.pk %}"><button class='btn btn-success'>Добавить коммент! </button></a>
</form>
</div>
{% else %}
<i>Вы не можете писать комментарии, необходимо зарегистрироваться!</i>
{% endif %}
{% endblock %}
So, in this template, where "userprofile.avatar.url" should be object reference on User avatar. I tryed a lot of things, but it always the same:not displaying
You should do:
<img src="{{ comment.author.userprofile.avatar.url }}" alt="user-avatar" width="100px" height="100px">
Your comment has a foreign key to User (author), and User has a one to one field to UserProfile, which is the one that has the avatar attribute.
Also another tip:
You shouldn't really reduce the image in CSS (width: 100px; height: 100px;), but instead use a tool that allows you to create thumbnails of images. I use sorl-thumbnail and can't recommend it enough.
The reason is that if every user uploads a 1000x1000 image, you are downloading those big images that you don't really need, hence your site will be slower.
Maybe you should try accesing the User object in the template, not the Userprofile.
<img src="{{ user.userprofile.avatar.url }}" ...

Django save only first form of formset

I've looked through every similar question (and tried them), but still couldn't find answer.
I have two models:
class Project(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, default=1)
name = models.CharField(max_length=120, verbose_name = "Название проекта")
url = models.URLField(max_length=120, unique=True, verbose_name = "Полный адрес сайта")
robots_length = models.CharField(max_length=5, default=0)
updated = models.DateTimeField(auto_now=True, auto_now_add=False)
timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
def __unicode__(self):
return self.name
def __str__(self):
return self.name
def get_absolute_url(self):
from django.urls import reverse
return reverse('projects:detail', args=[str(self.id)])
class ProjectPage(models.Model):
page_project = models.ForeignKey(Project, on_delete=models.CASCADE)
page_url = models.URLField(verbose_name = "Адрес страницы")
page_title = models.CharField(max_length=300, blank=True, verbose_name = "meta-title",default="")
page_description = models.CharField(max_length=300, blank=True, verbose_name = "meta-description",default="")
page_h1 = models.CharField(max_length=300, blank=True, verbose_name = "Заголовок h1",default="")
def __unicode__(self):
return self.page_url
def __str__(self):
return self.page_url
For each model there is a form:
class ProjectFormUpdate(forms.ModelForm):
class Meta:
model = Project
fields = [
"name",
"url",
]
widgets = {
'name': forms.TextInput(attrs={'placeholder': 'Произвольное название'}),
}
class ProjectPageForm(forms.ModelForm):
class Meta:
model = ProjectPage
fields = [
'page_project',
'page_url',
'page_title',
'page_description',
'page_h1',
]
widgets = {
'page_project': forms.HiddenInput()
}
In views.py I have:
def projects_update(request, proj=None):
instance = get_object_or_404(Project, id=proj)
form = ProjectFormUpdate(request.POST or None, instance=instance)
formset_f = modelformset_factory(ProjectPage, form=ProjectPageForm, extra=3)
formset = formset_f(queryset=ProjectPage.objects.filter(page_project__id=proj), initial =[{'page_project': proj}])
if request.method == 'POST':
formset = formset_f(request.POST)
for formset_form in formset:
if formset_form.is_valid() and formset_form.has_changed():
formset_form.save()
if form.is_valid():
form.save()
context = {
'title': "Редактируем проект - "+instance.name,
'form': form,
'formset': formset,
'instance': instance,
}
return render(request, "projects_update.html", context)
And, finaly, html
<form method="POST" action="" class="create-form">
{{ formset.management_form }}
{% csrf_token %}
<div class="row">
<div class="col-lg-6 offset-lg-3 col-md-10 offset-md-1 col-xs-10 offset-xs-1 form-bg">
<h2>Общие данные</h2>
{{ form|crispy}}
<input type="submit" class="btn btn-success" value="Обновить проект" />
</div>
</div>
{% for formset_form in formset %}
<div class="row form-container">
<div class="col-lg-6 offset-lg-3 col-md-10 offset-md-1 col-xs-10 offset-xs-1 form-bg">
<h3>Страница {{forloop.counter}}</h3>
{{ formset_form|crispy}}
</div>
</div>
{% endfor %}
</form>
What I am trying to achieve is: when user enters a page, he gets a form with project name and project URL already filled in. So, he can correct them.
Below, I want to show a filled in form for every page allready created for this project and several empty forms for creating new.
What happens is all initial data is displayed correctly, but when I fill several empty forms - only first empty form is saved each time.
Here is how it was solved:
Included errors properly.
Saw that second to last form lack required field (hiddenInput)
Made changes in view so it looks like:
formset_f = modelformset_factory(ProjectPage, form=ProjectPageForm, extra=3)
formset = formset_f(queryset=ProjectPage.objects.filter(page_project__id=proj), initial =[{'page_project': proj}, {'page_project': proj}, {'page_project': proj}])
Initial values now match number of extra forms - every form got it's own foreign key.
Probably there is a better solution, but the the problem is found and solved for me!
My problem was that when I tried to render every form of the formset manually I added an unneded <form></form> html element
wrong:
{ form.management_form }}
{% for form in formset %}
<form class="form-class">
{{form.name}}
</form>
right:
{ form.management_form }}
{% for form in formset %}
<div class="form-class">
{{form.name}}
</div>
After that change my forms were recognized correctly.

Categories