I have these two models :
class Home(db_interface.Entity):
patient = models.ForeignKey(Patient, help_text='Patient')
sode = models.ForeignKey(Sode, help_text='sode')
class Sode(db_interface.Entity):
start_date = models.DateField(validators=[validate_date_range], null=True, help_text="Start Date")
path = models.ForeignKey('Path', null=True, blank=True)
help_text="Path")
and my view is inherited from a ListView of Django .To return a queryset I override get_queryset() method :
def get_queryset(self):
instance = self.get_instance()
if instance:
return Sode.objects.filter(patient=instance).select_related('path').prefetch_related(
'home_set')
return []
to list data in my template I use a for loop :
{% for h in sode.home_set.all %}
data is displayed in lists perfectly, but what I want to do now is to filter the list (not the standard filter approach) I mean the items in the list will be modified depending of the status of some items .
I tried to write a custom filter so that the every time my template is loaded I have data in the list are shown as I want. But I recognized that in my filter I used objects from database which must be in the view to separate presentation code from data logic, so filtering models musn't be in a filter
I ended up by moving the logic I wrote in the custom filter to a function in my model's ListView
def sort_homes_per_sode(self,sode):
homes = sode.get_queryset()
#some logic here
return list_homes_filtred_depending_on_the_type_of_some_homes_of_sode
I don't know if I'm in a good way, and still don't know if the function is usefull or not and how to show data in template and associate every filtered list of homes to the corresponding sode
Related
I'm trying to replicate Blood Group as Model as defined in this picture.
.
In my models.py file I had my code to replicate the blood groups like this
class BloodGroup(models.Model):
name = models.CharField(
max_length=3
)
gives = models.ManyToManyField("self")
receives = models.ManyToManyField("self")
def __str__(self):
return self.name
And in my admin.py file I had registered the model as follows
class BloodGroupAdmin(admin.ModelAdmin):
model = BloodGroup
list_display = ['name', 'get_gives', 'get_receives']
def get_gives(self, obj):
return ", ".join([item.name for item in obj.gives.all()])
def get_receives(self, obj):
return ", ".join([item.name for item in obj.receives.all()])
admin.site.register(BloodGroup, BloodGroupAdmin)
Initially I created plain BloodGroup objects without their gives and receives attribute by providing just their names alone. Thus I create an object for all 8 types. Then as I added relationships to each object I found that adding gives or receives for one object affects other objects gives and receives too, making it impossible to replicate the structure in image.
How do I define relationships, without affecting other objects?
In my admin site, I see field names as "get_gives" and "get_receives". How would i make the admin page show field names as "gives" and "receives" but still displaying objects as strings like the image below?
For first question, probably it is better to have only one relation gives. receives can be found from the reverse query. Like this:
class BloodGroup(models.Model):
name = models.CharField(
max_length=3
)
gives = models.ManyToManyField("self", related_name="receives", symmetrical=False)
Then you only need to add objects to gives. receives will be generated automatically.
For second question, add short_description attribute to function(reference to docs). Like this:
get_gives.short_description = 'Gives'
get_receives.short_description = 'Receives'
I have a model which creates Memo objects. I would like to use a custom Model Manager's posted method to return the total number of Memo objects - then use this number within a template. I am trying to keep as much of my code as possible within my Models and Model Managers and less within my Views as I read that this was a best practice in 'Two Scoops of Django'.
In the shell I can get the number of memos as such:
>>> from memos.models import Memo
>>> Memo.objects.all()
<QuerySet [<Memo: Test Memo 2>, <Memo: Test Memo 1>]>
>>> Memo.objects.all().count()
2
This is what my Model and Model Manager look like:
class MemoManager(models.Manager):
use_for_related_fields = True
def posted(self):
return self.count()
class Memo(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date_time = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
objects = MemoManager()
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('memos-detail', kwargs={'pk': self.pk})
I know this is clearly the wrong way to do it but I have confused myself here. So how do I use my Model Manager to get the count of objects and use it in a template like: {{ objects.all.count }}?
P.S. I see other posts that show how to do this within the view but as stated I am trying not to use the view. Is using the view required? I also understand my posted method is written incorrectly.
I'm sorry but you have misinterpreted what was written in TSD. The Lean View Fat Model is meant to keep code which pertains to 'business logic' out of the views, and certain model specific things. A request should be handled by a view. So when you want to load a template, you must first have a GET request to your app.
A view function should be written such that Validation of POST data or the Creation of a new object in DB or Querying/Filtering for GET requests should be handled in the corresponding serializer/model/model manager.
What should be happening while you want to load your template.
Have a url for the template that you have created and a view function mapped for it
In the view function you should render said template and pass the necessary data inside the context.
To keep in line with the Lean View Fat Model style, if you want to get a Queryset of of Memo's but only those which have their is_deleted fields set to False, you can overwrite the model manager get_queryset() method for Memo model.
If you want to create a new Memo with a POST request, you can handle
the creation using a ModelForm!
Hope this clears things up!
EDIT:
How to pass a context to a template, in your case the memo count.
def random_memo_view(request):
context = {'memo_count': Memo.posted()}
return render(request, 'template.html', context=context)
RE-EDIT
I just checked that you were using DetailView. In this case follow this from the django docs.
Class Based Views: Adding Extra Context
I have a view that allows me to work with two different models at once, thanks to itertools chain. I'm rendering the instances of the two chained models inside a table in my template, and I'd need the rows of the table to be formatted differently in case the instances are from one model as opposed to the other.
So basically: I'm chaining two models and displaying their instances in a table, and all the rows of the table that contain instances from model A should be formatted with a yellow background and all the rows containing instances from model B should have a blue background instead.
This is the view:
class BaseView(generic.ListView):
template_name = 'base/base_list.html'
context_object_name = 'base_list'
def get_queryset(self):
queryset = Document.objects.order_by('due_date')
return queryset
def get_context_data(self, **kwargs):
context = super(BaseView, self).get_context_data(**kwargs)
context['object_list'] = sorted(
itertools.chain(Program.objects.all(), Document.objects.all()),
key=attrgetter('validity_date'),
reverse=True)
return context
In logic, what I'd need in the template would be something like this:
if
object in object_list ***belongs*** to Program.objects.all()
(etc)
else
(etc)
The question is: how should I express that belongs?
I've also looked into template tags but could not find the right way to go.
Thank you in advance.
As I mentioned in the comments, you should look for a way of identifying the model itself rather than checking if it is in a list. There is a built-in way of accessing the model name, but unfortunately that is inside the _meta attribute and you're not allowed to use attributes that start with underscores in a template.
So instead I would recommend simply adding one to your class definitions:
class Program(models.Model):
model_name = 'Program'
...
Now you can just do:
{% if object.model_name == 'Program' %}
...
{% else %}
...
{% endif %}
I am trying to find the best way to design the following search page in my website:
I have a database of pre-calculated measurements for a huge number of neurons.
Each measurment object holds more than 20 fields for each neuron.
The search page should provide a range of two values [min-max] for each fieldin the measurment object
For example (a simplified model):
class Measurment(models.Model):
width = models.FloatField('Width', null=True, blank=True)
height = models.FloatField('Height', null=True, blank=True)
depth = models.FloatField('Depth', null=True, blank=True)
the search page should have 6 textboxes (2 for each field)
The situation is that my measurments have a lot of fields, and also are not finished yet, i.e. they are still under test and some of them maybe excluded and other may be added so I don't want to change the search page everytime I try different fields in the measurement.
So I an idea that the inputs in the search page should be generated automatically according to the fields in the measurement model, and to generate two inputs for each field.
I made a form for the Measurement model but this will give one input per field, so I had the idea of creating two forms in the search page one for the min_values and he other for the max_values but I couldn't get each of the via the POST data
the code:
if request.method == POST:
if 'btn_search' in request.POST:
form_min = MeasureSearchForm(request.POST)
min_values = form_min.save(commit=False)
form_max = MeasureSearchForm(request.POST)
max_values = form_max.save(commit=False)
I can't specify which form to get each time.
So what is the right designing way to acheive this search functionality?
Thank you.
Make a ModelForm based on your Measurement model. (say MeasurementModelForm)
Pass them as two different context variable.
context["form1"] = MeasurementModelForm() and context["form2"] = MeasurementModelForm()
Render both form in your template within the SAME form tag.
<form method="post" ....>
{{ form1 }}
{{ form2 }}
</form
Inside your view:
if request.method == POST:
form1_data = {key:query[key][0] for key in request.POST}
form2_data = {key:query[key][1] for key in request.POST}
form_min = MeasureSearchForm(form1_data)
form_max = MeasureSearchForm(form2_data)
# ...
OR
Use modelformsetfactory
Hope this helps.
So I have a model Listing() that has a field views. In my one of my views, when someone looks at the listing's page, the views field is incremented by one via listing.views = F('views') + 1 and listing.save(update_fields=['views']). My issue is that when I access the views attribute from that same template using {{ listing.views }}, instead of display the current amount of views, the template displays F(views) + Value(1) (literally that text). Now, I assume I could use a Model method such as def get_views() which will return self.views, but I was wondering why I am getting this weird issue. Also, is there a way without writing a model method that I can get the actual integer instead of the odd F(views) + Value(1)?
Here is my current code:
models.py
class Listing(models.Model):
...
views = models.IntegerField(default=0)
listings.py
class ListingView(View):
def get(self, request):
listing_id = request.GET['listing_id']
listing = get_object_or_404(Listing, id=listing_id)
listing.views = F('views') + 1
listing.save(update_fields=['views'])
return render(request, 'listings.html', {'listing': listing})
listings.html
<html>
{{ listing.views }}
</html>
Using F expressions like this requires you to re-fetch the item once saved in order to get updated values (due to the nature of the F expression updating at a database level and not the model instance itself; perhaps that's where the decrease in operational costs come in).
From the docs -
In order to access the new value that has been saved in this way, the object will need to be reloaded:
reporter = Reporters.objects.get(pk=reporter.pk)
# Or, more succinctly:
reporter.refresh_from_db()