I have a Django admin class which declares an inlines iterable. Something like:
#admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
...
...
inlines = [CategoryModifiersInline,]
...
...
Then I have an Inline admin class like this:
class CategoryModifiersInline(admin.TabularInline):
model = Category.modifiers.through
fk_name = 'category'
extra = 1
def formfield_for_foreignkey(self, db_field, request, **kwargs):
qs = Product.objects.filter(is_modifier=True).filter(active=True)
kwargs['queryset'] = qs
return super(CategoryModifiersInline, self).formfield_for_foreignkey(db_field, request, **kwargs)
Where I filter the queryset for the foreign key based on some business requirement.
This inline is only showed to the user in the change view, that means, when an object of the class Category is created and the user wants to add modifiers to it, never in the add view.
What I want to do, is filtering the foreign key by one of the attributes of the Category model, I mean, I want to access the parent object from the formfield_for_foreignkey method.
Does anyone know a way to achieve that?
Well I found a similar question here in StackOverflow, and used the method described there to solve it.
It uses the parent_model attribute from inlines, and the resolve method from django.core.urlresolvers to get the instance based in the url.
Here's the code:
def get_object(self, request):
resolved = resolve(request.path_info)
if resolved.args:
return self.parent_model.objects.get(pk=resolved.args[0])
return None
Then I would call the get_object method inside of my formfield_from_foreignkey method to get the instance of the object I want to use as a filter.
Hope it helps!
Related
There are several questions on stackoverflow related to this topic but none of them explains whats happening neither provides working solution.
I need to pass user's first name as an argument to Django ModelForm when rendering template with this form.
I have some basic form:
class MyForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
self.first_name = ???
super(MyForm, self).__init__(*args, **kwargs)
class Meta:
model = MyModel
fields = [***other fields***, 'first_name']
Here's my sample class-based view:
class SomeClassBasedView(View):
def get(self, request):
user_first_name = request.user.first_name
form = MyForm(user_first_name)
return render(request, 'my_template.html', {'form': form})
What do I pass to MyForm when initialising it and how do I access this value inside the __init__ method?
I want to use 'first_name' value as a value for one of the fields in template by updating self.fields[***].widget.attrs.update({'value': self.first_name}).
The solution is as simple as passing initial data dictionary to MyForm for fields you need to set initial value.
This parameter, if given, should be a dictionary mapping field names to initial values. So if MyModel class has a field my_field and you want to set its initial value to foo_bar then you should create MyForm object with initial parameters as follows:
form = MyForm(initial={'my_field': 'foo_bar'})
A link to Django documentation on this topic.
You can pass the parameter instance to your form like this:
obj = MyModel(...)
form = MyForm(instance=obj)
If obj has a user first name it will be attached to your form.
Lets say i have a model like so:
class MyModel(models.Model):
first_field = models.CharField()
second_field = models.CharField()
and an API view like so:
class MyModelDetailAPI(GenericAPIView):
serializer_class = MyModelSerializer
def patch(self, request, *args, **kwargs):
# Do the update
def post(self, request, *args, **kwargs):
# Do the post
The first_field is a field that is only inserted in the POST method (and is mandatory) but on each update, the user can't change its value so the field in the PATCH method is not mandatory.
How can i write my serializer so that the first_field is required on POST but not required on PATCH. Is there any way of dynamically setting the required field so i can still use the DRF validation mechanism? Some sort of validator dispatcher per request method?
I want something like this for example:
class MyModelSerializer(serializers.ModelSerializer):
class Meta:
model = MyModel
fields = {
'POST': ['first_field']
'PATCH': []
}
I need more space than comments provide to make my meaning clear. So here is what I suggest:
Different formatting means different serializers.
So here you have, for instance a MyModelSerializer and a MyModelCreationSerializer. Either create them independently, or have one inherit the other and specialize it (if it makes sense).
Use the appropriate GenericAPIView hook to return the correct serializer class depending on self.action. A very basic example could be:
class MyModelDetailAPI(GenericAPIView):
# serializer_class = unneeded as we override the hook below
def get_serializer_class(self):
if self.action == 'create':
return MyModelCreationSerializer
return MyModelSerializer
Default actions in regular viewsets are documented here, they are:
create: POST method on base route url
list: GET method on base route url
retrieve: GET method on object url
update: PUT method on object url
partial_update: PATCH method on object url
destroy: DELETE method on object url
I have two models, Fridge and Product. Fridge has a ForeignKey to auth.user, while Product has a ForeignKey to Fridge, simple enough.
Now quite obviously, I want an user to be able to add products only to his fridge. I want to create an API for that using DRF, AND I want a nice dropdown in the product viewset form, hence why I have to do this in a serializer. I'm using viewsets.ViewSet class, so get_serializer_context doesn't seem to have any effect.
class ProductSerializer(serializers.Serializer):
pk = serializers.IntegerField(read_only=True)
fridge = serializers.PrimaryKeyRelatedField(queryset=WHAT_QUERYSET)
name = serializers.CharField(max_length=Product._meta.get_field('name').max_length)
purchase_date = serializers.DateField()
before_date = serializers.DateField()
I have no idea what should I put in the queryset keyword argument for the fridge attribute. I can't ask for request.user anywhere in that scope, as no context or request variables exist at that point.
What you need is dynamically queryset based on the user that makes the request. You can achieve this by overriding the __init__ method like this:
def __init__(self, *args, **kwargs):
super(ProductSerializer, self).__init__(*args, **kwargs)
request_user = self.context['request'].user
self.fields['fridge'].queryset = Fridge.objects.filter(user=request_user)
The initial field will have to look like this, since you have to specify the queryset othewise you will get an AssertionError:
fridge = serializers.PrimaryKeyRelatedField(queryset=Fridge.objects.all())
but this will be overridden of course.
I have a view (CreateWorkRelationView) that makes use of the CreateView CBV. In the URL, a parameter is passed (user) that I need to refer to a lot. Is it possible to set the object user outside the functions in my class? So are you able to access kwargs from outside a function?
So I basically just want to add the following line to my class
user = get_object_or_404(Contact.pk=kwargs['user'])
At the moment however, that returns
NameError: name 'kwargs' is not defined
This is my class
class CreateWorkRelationView(LoginRequiredMixin, SuccessMessageMixin, CreateView):
template_name = 'groups/group_form.html'
form_class = WorkRelationForm
model = WorkRelation
title = "Add a work relation"
success_message = "Workrelation was successfully created."
def form_valid(self, form):
user = get_object_or_404(Contact, pk=self.kwargs['user'])
form.instance.contact = user
return super(CreateWorkRelationView, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('contacts:contact_detail', self.kwargs['user'])
The reason why I would like to do this, is:
I want to use this object in my title string.
I am going to add a couple of more functions, and they all need this object.
The way I managed to do this is to use a FormView.
In my urls.py i have
regex=r'^my/path/(?P<pk>\d+)$',
In my views
class MyCreateView(LoginRequiredMixin, FormView):
def form_valid(self, form):
data = self.kwargs['pk']
It works well.
No, that can't possibly work; you don't have a user, or kwargs, or even a request at the time the class is defined. You need to do this inside one of the methods called at request time; probably get_context_data or get_object.
I have a model, Foo. It has several database properties, and several properties that are calculated based on a combination of factors. I would like to present these calculated properties to the user as if they were database properties. (The backing factors would be changed to reflect user input.) Is there a way to do this with the Django admin interface?
I would suggest you subclass a modelform for Foo (FooAdminForm) to add your own fields not backed by the database. Your custom validation can reside in the clean_* methods of ModelForm.
Inside the save_model method of FooAdmin you get the request, an instance of Foo and the form data, so you could do all processing of the data before/after saving the instance.
Here is an example for a model with a custom form registered with django admin:
from django import forms
from django.db import models
from django.contrib import admin
class Foo(models.Model):
name = models.CharField(max_length=30)
class FooAdminForm(forms.ModelForm):
# custom field not backed by database
calculated = forms.IntegerField()
class Meta:
model = Foo
class FooAdmin(admin.ModelAdmin):
# use the custom form instead of a generic modelform
form = FooAdminForm
# your own processing
def save_model(self, request, obj, form, change):
# for example:
obj.name = 'Foo #%d' % form.cleaned_data['calculated']
obj.save()
admin.site.register(Foo, FooAdmin)
Providing initial values for custom fields based on instance data
(I'm not sure if this is the best solution, but it should work.)
When a modelform for a existing model instance in the database is constructed, it gets passed this instance. So in FooAdminForm's __init__ one can change the fields attributes based on instance data.
def __init__(self, *args, **kwargs):
super(FooAdminForm, self).__init__(*args, **kwargs)
# only change attributes if an instance is passed
instance = kwargs.get('instance')
if instance:
self.fields['calculated'].initial = (instance.bar == 42)
It's easy enough to get arbitrary data to show up in change list or make a field show up in the form: list_display arbitrarily takes either actual model properties, or methods defined on the model or the modeladmin, and you can subclass forms.ModelForm to add any field type you'd like to the change form.
What's far more difficult/impossible is combining the two, i.e. having an arbitrary piece of data on the change list that you can edit in-place by specifying list_editable. Django seems to only accept a true model property that corresponds to a database field. (even using #property on the method in the model definition is not enough).
Has anyone found a way to edit a field not actually present on the model right from the change list page?
In the edit form, put the property name into readonly_fields (1.2 upwards only).
In the changelist, put it into list_display.
You can use the #property decorator in your model (Python >= 2.4):
class Product(models.Model):
#property
def ranking(self):
return 1
"ranking" can then be used in list_display:
class ProductAdmin(admin.ModelAdmin):
list_display = ('ranking', 'asin', 'title')