Can I dynamically set field names in Django orm query? - django

Can I dynamically set field names in Django view?
I want this code
CategoryNick.objects.get(author=self.request.user).get(field=slug)
but error is occured
AttributeError: 'CategoryNick' object has no attribute 'get'
Is there a good way to solve this problem?
if you know solution thank you for let me know
total code
def get_context_data(self, *, object_list=None, **kwargs):
context = super(type(self), self).get_context_data(**kwargs)
context['posts_without_category'] = MyShortCut.objects.filter(category=None,author=self.request.user).count()
context['category_list'] = Category.objects.all()
slug = self.kwargs['slug']
if slug == '_none':
context['category'] = 'no_category'
else:
category = Category.objects.get(slug=slug)
context['category'] = category
context['category_nick'] = CategoryNick.objects.get(author=self.request.user).get(field=slug)
return context

Instead of doing like this CategoryNick.objects.get(author=self.request.user).get(field=slug) you can do like this CategoryNick.objects.get(author=self.request.user, field=slug)

If you want to retrieve the field of the object, you can use .values_list(..., flat=True) [Django-doc] here, like:
CategoryNick.objects.values_list('slug', flat=True).get(author=self.request.user)
So here you will retrieve the slug value for that CategoryNick. If you use 'pk' instead, you will get the primary key.
It is however a bit "odd" to just query for a given field name.

Related

New to Django - get list of users with the most posts

First post and new to python and django.
I am trying to add context to a ListView so that I can display a list of top 5 most commented posts and also a list of top 5 most active users (users with most posts). I have got the first list working however can't work out the second list. Using the values method (i think it's a method) i've managed to query the database and get back a dictionary of user_id and posts count. My problem is that I want to display the username rather than the user_id. For some reason this query results in a dictionary rather than the full object. I've posted the code from views.py below.
class PostListView(ListView):
queryset = Article.objects.filter(published=True)
template_name = 'articles/index.html' #address of non default template
context_object_name = 'articles' #variable name passed to template
ordering = ['-pub_date']
#get query results for popular posts and most active users side bar
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['most_commented'] = Article.objects.annotate(comment_count=Count('comments')).order_by('-comment_count')[:5]
context['most_active'] = Article.objects.all().values('author').annotate(active=Count('author')).order_by('-active')[:5]
return context
Thank you for your help!
Nick
You're correct, values is a method that returns dicts. If you want the full object, there's no need to use values here:
context['most_active'] = Article.objects.all().annotate(active=Count('author')).order_by('-active')[:5]
You can then iterate through most_active in the template and access the username directly.
If you did want to use values, just pass the fields you want returned in the dict:
context['most_active'] = Article.objects.all().values('author__username').annotate(active=Count('author')).order_by('-active')[:5]
I think you are making it too complicated. You can annotate the User to obtain the most Users with the most published articles:
class PostListView(ListView):
queryset = Article.objects.filter(published=True)
template_name = 'articles/index.html' #address of non default template
context_object_name = 'articles' #variable name passed to template
ordering = ['-pub_date']
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['most_commented'] = Article.objects.annotate(
comment_count=Count('comments')
).order_by('-comment_count')[:5]
context['most_active'] = User.objects.annotate(
narticles=Count('article')
).order_by('-narticles')[:5]
return context

Django FilterSet set initial value

We are using Django_Filterset in our project. I have been asked to set a default filter value for a foreign key column in the model
class RegFilter(django_filters.FilterSet):
class Meta:
model = models.Reg
fields = {
'id': ['exact'],
'nom_id': ['icontains'],
'nom_name': ['icontains'],
'product__name': ['icontains']
}
The product name should default to a product already in the db when the initial screen is displayed - any idea how this can be achieved? Appreciate your help.
I built on the answer given by #Gayathri and fixed a few small issues. He had a typo in his code, and the code wouldn't work with multiple choice fields, since they require the MultiValueDict functionality for multiple entries.
Given a rather regular view, here is the full code (in Python3 syntax):
class BookListView(FilterView):
model = Book
filterset_class = BookFilter
def get_filterset_kwargs(self, filterset_class):
kwargs = super().get_filterset_kwargs(filterset_class)
if kwargs['data'] is None:
filter_values = MultiValueDict()
else:
filter_values = kwargs['data'].copy()
if not filter_values:
# we need to use `setlist` for multi-valued fields to emulate this coming from a query dict
filter_values.setlist('status', ['new', 'ready'])
filter_values['sorting'] = '-created'
kwargs['data'] = filter_values
return kwargs
I managed to solve this one and thought the solution might help someone else in a situation similar to me.
In the views.py, override the FilterView method
def get_filterset_kwargs(self, filterset_class):
kwargs = super(RegFilter, self).get_filterset_kwargs(filterset_class)
if kwargs['data'] is None:
request_dict = {}
else:
request_dict = kwargs['data'].dict()
# This default will not be populated if any other filter options are chosen to restrict the query set
if not request_dict:
request_dict.update({
'product__name': 'ABC Product'
})
request_dict = kwargs['data']
return kwargs
This should now set the default product as 'ABC product' when no other options are passed to restrict the data fetched.

pass multiple parameters to form from html table in django

I am newbie with Django and I get stucked trying to pass the value from a html table rendered with django-tables2 to a form.
view.py
def configView(request):
form = ConfigForm(request.POST or none)
if form.is_valid():
save_it = form.save(commit=False)
save_it.save()
Messages.success(request, 'Configuracion Actualizada')
return HttpResponseRedirect('/monitor/')
return render_to_response("config.html",
locals(),
context_instance=RequestContext(request))
This is my forms.py
class ConfigForm(forms.ModelForm):
class Meta:
model = Config
def __init__(self, *args, **kwargs):
super(ConfigForm, self).__init__(*args,**kwargs)
self.fields['id_proveedor'].initial = kwargs.pop('id_proveedor',None)
But I don't know how to retrieve and pass the value to theform.
I need pass the values from the cells 0, 2 and 6.
Any advice or snippet will be appreciated.
Thanks in advance
I would try this:
class ConfigForm(forms.Form):
def __init__(self, *args, **kwargs):
your_variable_to_pass = kwargs.pop("your_variable_to_pass")
super(ConfigForm, self).__init__(*args,**kwargs)
self.fields['id_proveedor']= forms.FieldClass(attribute=your_variable_to_pass)
id_proveedor = FieldClass()
where, 'FieldClass' is whatever field you choose (i.e. ChoiceField, CharField) and
attribute is the attribute to pass (your variable), i.e. 'choices', 'initial' etc.
thus, it may look like this:
self.fields['id_proveedor']= forms.ChoiceField(choices=your_variable_to_pass)
id_proveedor = ChoiceField()
Notice indentation - you assign value of the attribute to pass in the constructor!; in case of ChoiceField choices is a list of tuples, i.e. (('1', 'First',), ('2', 'Second',)); I use Forms instead of ModelForm as super or base class in this example
Then, in the views: f = ConfigFrom(request.POST, your_variable_to_pass=your_variable_to_pass)
notice your_variable_to_pass=your_variable_to_pass otherwise it'll generate a key error
I hope, it helps!

django forms: default values for bound forms

With this form:
class Form(forms.Form):
name = forms.CharField(required=False, initial='Hello world')
If I do something like this in the view:
form = Form(request.GET)
if form.is_valid():
name = form.cleaned_data['name']
Then initial value of name is lost even if request.GET does not contain name as key. Is there any workaround? I would like initial values work to bound forms as "default values".
By slightly modifying Gonzalo's solution, this is the right way:
class Form(forms.Form):
name = forms.CharField(required=False, initial='Hello world')
def clean_name(self):
if not self['name'].html_name in self.data:
return self.fields['name'].initial
return self.cleaned_data['name']
If you need this, you may have a look at django-filter app. I have discovered it quite recently.
initial isn't really meant to be used to set default values for form fields.
Instead, it's really more a placeholder utility when displaying forms to the user, and won't work well if the field isn't required (like in your example).
What you can do is define a clean_<fieldname> method that checks if there's an empty value for that field and return the default:
class Form(forms.Form):
name = forms.CharField(required=False, initial='Hello world')
def clean_name(self):
name = self.cleaned_data['name']
if name is None:
return self.fields['name'].initial
return name
I use the following pattern for setting default values as initial values given for the form-
class InitialDefaultForm(forms.Form):
def clean(self):
cleaned_data = super(InitialDefaultForm, self).clean()
# if data is not provided for some fields and those fields have an
# initial value, then set the values to initial value
for name in self.fields:
if not self[name].html_name in self.data and self.fields[name].initial is not None:
cleaned_data[name] = self.fields[name].initial
return cleaned_data
This ensures that all fields which have an initial value and do not get values from user get populated by their initial value.
request.GET is a dictionary like object.
initial only works in case of unbound form.
Forms have an attribute named data. This attribute is provided as first positional argument or as a data keyword argument during form initialization.
Bound forms are those in which you provide some data as first argument to the form and unbound form has data attribute set as None.
Here in your initialization of form form=Form(request.GET), you are providing the first positional argument, so data attribute is being set on the form and it becomes a bound form. This happens even if request.GET is an empty dictionary. And since your form becomes a bound form so initial of name field has no effect on it.
So, In you GET request you should either do:
form = Form()
and your initial of name field would be honoured.
Or, if you want to read name from request.GET and if its there then want to use it instead of field's initial then have following in your view.
name = request.GET.get(name)
form_level_initial = {}
if name:
form_level_initial['name'] = name
form = Form(initial=form_level_initial)
Will this work:
initial_form_data = {'name': 'Hello World'} #put all the initial for fields in this dict
initial_form_data.update(request.GET) #any field available in request.GET will override that field in initial_form_data
form = Form(initial_form_data)
if form.is_valid():
name = form.cleaned_data['name']
The proposed solutions either didn't work for me or just seemed not very elegant. The documentation specifies that initial does not work for a bound form, which seems to be the original questioners (and my) use case:
This is why initial values are only displayed for unbound forms. For bound forms, the HTML output will use the bound data.
https://docs.djangoproject.com/en/1.10/ref/forms/fields/#initial
My solution is to see if the form should be bound or not:
initial = {'status': [Listing.ACTIVE], 'min_price': 123} # Create default options
if request.method == 'GET':
# create a form instance and populate it with data from the request:
if len(request.GET):
form = ListingSearchForm(request.GET) # bind the form
else:
form = ListingSearchForm(initial=initial) # if GET is empty, use default form
You could also use the other ways of initializing the form (mentioned above).
None of the answers actually does exactly what clime asked for. So here is my solution for the same problem:
class LeadsFiltersForm(forms.Form):
TYPE_CHOICES = Lead.TYPES
SITE_CHOICES = [(site.id, site.name) for site in Site.objects.all()]
type = forms.MultipleChoiceField(
choices=TYPE_CHOICES, widget=forms.CheckboxSelectMultiple(),
required=False
)
site = forms.MultipleChoiceField(
widget=forms.CheckboxSelectMultiple(), required=False,
choices=SITE_CHOICES
)
date_from = forms.DateField(input_formats=['%m-%d-%Y',], required=False,
widget=forms.TextInput(attrs={'placeholder': 'Date From'}),
initial=timezone.now() - datetime.timedelta(days=30))
date_to = forms.DateField(input_formats=['%m-%d-%Y',], required=False,
widget=forms.TextInput(attrs={'placeholder': 'Date To'}))
defaults = {
'type': [val[0] for val in TYPE_CHOICES],
'site': [val[0] for val in SITE_CHOICES],
'date_from': (timezone.now() - datetime.timedelta(days=30)).strftime('%m-%d-%Y'),
'date_to': timezone.now().strftime('%m-%d-%Y')
}
def __init__(self, data, *args, **kwargs):
super(LeadsFiltersForm, self).__init__(data, *args, **kwargs)
self.data = self.defaults.copy()
for key, val in data.iteritems():
if not data.get(key):
continue
field = self.fields.get(key)
if field and getattr(field.widget, 'allow_multiple_selected', False):
self.data[key] = data.getlist(key)
else:
self.data[key] = data.get(key)

forms ModelChoiceField queryset + extra choice fields django forms

I am trying to create a form in that ModelChoiceField loads from queryset and i want add few custom values to ModelChoiceField for extend i have used choice field, like below but while updating the form,getting below error
Form Error :
Select a valid choice. That choice is not one of the available choices.
Code :
self.fields['lead'] = forms.ModelChoiceField(queryset = Pepole.objects.filter(poc__in = ('lead','sr.lead')))
self.fields['lead2'] = forms.ModelChoiceField(queryset = Pepole.objects.filter(role__in = ('lead','sr.lead')))
choice_field = self.fields['lead']
choice_field.choices = list(choice_field.choices) + [('None', 'None')]
choice_field = self.fields['lead2']
choice_field.choices = list(choice_field.choices) + [('None', 'None')]
Am i doing any thing wrong here?
That's not going to work. Look at how a ModelChoiceField works:
try:
key = self.to_field_name or 'pk'
value = self.queryset.get(**{key: value})
except self.queryset.model.DoesNotExist:
raise ValidationError(self.error_messages['invalid_choice'])
return value
You can't add something randomly to it.
Use a ChoiceField instead and custom process the data.
class TestForm(forms.Form):
mychoicefield = forms.ChoiceField(choices=QS_CHOICES)
def __init__(self, *args, **kwargs):
super(TestForm, self).__init__(*args, **kwargs)
self.fields['mychoicefield'].choices = \
list(self.fields['mychoicefield'].choices) + [('new stuff', 'new')]
def clean_mychoicefield(self):
data = self.cleaned_data.get('mychoicefield')
if data in QS_CHOICES:
try:
data = MyModel.objects.get(id=data)
except MyModel.DoesNotExist:
raise forms.ValidationError('foo')
return data
It appears that you just want to allow those form fields to be optional. Don't make it hard on yourself. See the documentation regarding marking a form field as required.
lead = forms.ModelChoiceField(queryset=People.objects.filter(poc__in=('lead', 'sr.lead')), required=False)
Do you have a Person object with pk 'None'?
i think you should be using
self.fields['lead'] = forms.ModelChoiceField(queryset = Pepole.objects.filter(poc__in = ('lead','sr.lead')), empty_label="None")
self.fields['lead2'] = forms.ModelChoiceField(queryset = Pepole.objects.filter(role__in = ('lead','sr.lead')), empty_label="None")
edit:
Since you are using a modelchoicefield, i would think that all your choices would be either of that model type or none.
You can "extend" the choices of that type by modifying the queryset you pass into the constructor for the modlechoicefield, e.g.:
qs = People.objects.filter(poc__in = ('lead','sr.lead'))
ext = People.objects.filter(role__in = ('lead', 'sr.lead'))
qs = qs | ext
self.fields['lead'] = forms.ModelChoiceField(queryset = qs, empty_label='None')
or for updating
self.fields['lead'].queryset = qs
this question talks about the modelchoicefield a bit and might be of interest to you:
How do I filter ForeignKey choices in a Django ModelForm?