Create custom Values method in django - django

I want to create a method semi to values method in Django QuerySet.
The values method problems are:
Miss order of fields in querySet if I make myquery = MyModel.objects.values('field1','field2','field3')
when I print querSet it give me [{'field2':'data','field1':'data','field3':'data'},...]. so this miss order will cause a problem at Union of queryset.
if the field is choices at model then values(...) will give me the key of dictionary instead of its value.
I want to create my custom values method using django Manager
class MyModelManager(models.Manager):
def values(self, *fields):
# I want to get model belong to this manger
# then I want to check the fields if its in models (we have fields came from annotate)
# after that I want to exclude to the fields that has choices
# next I want to call the super values method and passed to it the values that does not have choices
# finally I want to reorder query according to passed fields
class MyModel(models.model):
# An example model
objects = MyModelManager()

Related

Django: Create Choices For Filter Based on Distinct Model Values

Objective: I want to have a ModelChoiceFilter rendered on the page where the options/choices for the user to select are the distinct values of a field in the model.
The app allows users to track the cities that they have visited. I want users to be able to filter the model on country and for the filter options to only be countries that are in the model.
I am using django-filters and do successfully have a MultipleChoiceFilter working where the choices work when hard-coded (either in the models.py or in the FilterForm class:
class cityFilter(django_filters.FilterSet):
continent = MultipleChoiceFilter(
choices=cityModel.continent_choices,
widget=forms.CheckboxSelectMultiple,
label='Continent')
class Meta:
model = cityModel
fields = ['city', 'country', 'continent']
One can also set the choices directly in the FilterSet class like this:
country = MultipleChoiceFilter(
choices=(('GB','United Kingdom'),('FR','France')),
widget=forms.CheckboxSelectMultiple,
label='Country'
)
This works okay for continents but I want to allow a filter for countries. I only want to expose to the user the distinct set of countries in the model (as opposed to hard coding every country).
I could do something like this to get all the distinct countries in the model:
country_choices = cityModel.objects.values('country').distinct()
But I'm not sure where this should be placed in the app to get queried on the page load, and would then need to take each value in the queryset and iterate to turn it into a 'choice' tuple.
Is there a better pattern/approach?
If you want dynamic choices and the choices data from Model, you can try to use queryset in the ModelMultipleChoiceFilter like this:
continent = ModelMultipleChoiceFilter(
queryset=cityModel.objects.values('country').distinct(),
widget=forms.CheckboxSelectMultiple,
label='Continent')

How I can get attribute from queryset using prefetch_related in Django?

I have following model and extracted queryset using prefetch_related as below.
queryset = Light.objects.filter(
certificate__name="A").prefetch_related('zone__namingzone'
)
From this queryset, I want to get following data set.
{"naming1":lpd1,"naming2":lpd2...}
However, when I try to extract attribute from queryset as below, I get create_reverse_many_to_one_manager
for i in queryset:
print (i.zone.namingzone)
What I want to get is naming attribute in naming table. Could anyone tell me how I can extract this?
models.py
class Certificate(models.Model):
name=models.CharField(max_length=20)
class Zone(models.Model):
zone=models.CharField(max_length=20)
class Light(models.Model):
certificate=models.ForeignKey(Certificate, on_delete=models.CASCADE,related_name='certificate')
zone=models.ForeignKey(Zone, on_delete=models.CASCADE,related_name='lightzone')
lpd=models.IntegerField()
class Meta:
unique_together = (('certificate', 'zone'),)
class Naming(models.Model):
zone=models.ForeignKey(Zone, on_delete=models.CASCADE,related_name='namingzone')
naming=models.CharField(max_length=20)
When you traverse a FK in reverse, you end up with a manager, and multiple items on the other side. So i.zone.namingzone in your for loop is a manager, not a NamingZone. If you change your print loop to:
for i in queryset:
print (i.zone.namingzone.all())
You should see all the naming zones for your item. You can extract the naming field from each NamingZone from the queryset as follows:
queryset.values('zone__namingzone__naming')
You probably want to extract a few other fields from your Light model, like the lpd for instance:
queryset.values('lpd', 'zone__namingzone__naming')
You might have a same ldp several times, as many times as it has naming zones.

Django restrict options of ManyToMany field in ModelForm based on model instance

I want to show only options already stored in models' ManyToManyField.
I have model Order which I want to have a Model based form like this:
class OrderForm(ModelForm):
class Meta:
model = Order
fields = ['amount', 'color']
Now I do not want to display all colors as choices, but instead only color instances saved in ManyToManyField of another model. The other model is Design:
class Design(models.Model):
color = models.ManyToManyField('maker.Color')
# ...
Is this at all possible while using ModelForm?
Attempt
I have tried doing it by having a ModelForm of Design and setting instance:
class ColorForm(ModelForm):
class Meta:
model = Design
fields = ['color']
And then in view:
color_form = ColorForm(instance=design)
But I don't exactly understand what setting instance does, and I think instance is not what I am looking for as it still lists all colors.
The instance setting has nothing to do with limiting the choices. In essence, it simply populates the form's values with the ones from a specific record. You usually provide an instance in an edit operation, whereas you skip it in an add operation.
The representation of a models.ManyToManyField in the ModelForm is a forms.ChoiceField for which you can simply override its queryset property, and specify the queryset you desire.
Therefore, in your view:
form = OrderForm()
form.fields['color'].queryset = Design.object.all() # for example

How to Stop Django ModelForm From Creating Choices for a Foreign Key

I have a Django model with a Foreign key to a table that contains about 50,000 records. I am using the Django forms.ModelForm to create a form. The problem is I only need a small subset of the records from the table the Foreign key points to.
I am able to create that subset of choices in the init method. How can I prevent ModelForm from creating that initial set of choices?
I tried using the widgets parameter in the Meta method. But Django debug toolbar indicates the database is still being hit.
Thanks
The autogenerated ModelChoiceField will have its queryset initialized to the default. The widget is not where you are supposed to customize the queryset property.
Define the ModelChoiceField manually, initialize its queryset to be empty. Remember to name the ModelChoiceField the same as the one that would have been automatically generated, and remember to mention that field in the fields tuple. Now you can set the queryset from the constructor and avoid the database being hit twice.
If you are lucky (and you probably are, please test though), the queryset has not been evaluated during construction, and in that case, defining the ModelChoiceField manually is not required.
class YourModelForm(ModelForm):
your_fk_field_name = forms.ModelChoiceField(queryset=YourModel.objects.none())
class Meta:
model = YourModel
fields = ('your_fk_field_name', .......)
def __init__(self, *args, **kwargs):
super(YourModelForm, self).__init__(*args, **kwargs)
self.fields['your_fk_field_name'].queryset = ....

Django Forms - set field values in view

I have a model which represents a work order. One of the fields is a DateField and represents the date the work order was created (aptly named: dateWOCreated). When the user creates a new work order, I want this dateWOCreated field to be automatically populated with todays date and then displayed in the template. I have a few other fields that I want to set without user's intervention but should show these values to the user in the template.
I don't want to simply exclude these fields from the modelForm class because there may be a need for the user to be able to edit these values down the road.
Any help?
Thanks
When you define your model, you can set a default for each field. The default object can be a callable. For your dateWOCreated field we can use the callable date.today.
# models.py
from datetime import date
class WorkOrder(models.Model):
...
dateWOCreated = models.DateField(default=date.today)
To display dates in the format MM/DD/YYYY, you need to override the widget in your model form.
from django import forms
class WorkOrderModelForm(forms.ModelForm):
class Meta:
model = WorkOrder
widgets = {
'dateWOCreated': forms.DateInput(format="%m/%d/%Y")),
}
In forms and model forms, the analog for the default argument is initial. For other fields, you may need to dynamically calculate the initial field value in the view. I've put an example below. See the Django Docs for Dynamic Initial Values for more info.
# views.py
class WorkOrderModelForm(forms.ModelForm):
class Meta:
model = WorkOrder
def my_view(request, *args, **kwargs):
other_field_inital = 'foo' # FIXME calculate the initial value here
form = MyModelForm(initial={'other_field': 'other_field_initial'})
...