In relation to the question below, how would I do this for the django admin?
Add data to ModelForm object before saving
Example:
class FooInlineModel(models.Model):
bar = models.CharField()
secret = models.CharField()
class FooInlineForm(forms.ModelForm):
class Meta:
model = FooInlineModel
exclude = ('secret',)
class FooInline(admin.TabularInline):
pass
class FooAdmin(admin.ModelAdmin):
inlines = (FooInline,)
This logically triggers an IntegrityError because secret is submitted NULL, how do I manually populate the field in one of the admin methods? e.g.
class ???:
def ???:
obj = form.save(commit=False)
obj.secret = 'stackoverflow'
obj.save()
Alright I found the method, for those interested:
class FooAdmin(admin.ModelAdmin):
inlines = (FooInline,)
def save_formset(self, request, form, formset, change):
instances = formset.save(commit=False)
for instance in instances:
if isinstance(instance, FooInlineModel):
instance.secret = 'stackoverflow'
instance.save()
Related
I have an exam like this:
class Exam(BaseModel):
...
STATE_CHOICES = (
(PASS, PASS),
(FAILED, FAILED),
(GREAT, GREAT),
state = models.CharField(max_length=15, choices=STATE_CHOICES, default=PASS)
...
Inside Django admin, I want the user with group X to be able to only change the state only from FAILED to PASS.
and users with group Y be able to change the state from FAILED to PASS and PASS to GREAT.
here is my admin.py:
#admin.register(Exam)
class ExamAdmin(NestedModelAdmin):
list_display = ('state',)
Does anyone know a solution for it?
This might work;
class AdminExamForm(forms.ModelForm):
...
options
...
class ExamForm(forms.ModelForm):
...
STATE_CHOICES = (
(PASS, PASS),
(FAILED, FAILED),
(GREAT, GREAT),
state = forms.CharField(choices=STATE_CHOICES)
class Meta:
model = Exam
fields = ('state',)
...
#admin.register(Exam)
class ExamModelAdmin(admin.ModelAdmin):
...
fields = ('state',)
list_display = ('state',)
form = ExamForm
...
def get_form(self, request, obj=None, **kwargs):
if request.user.is_admin or request.user.is_superuser:
return AdminExamForm
else:
return ExamForm
Sorry for giving you a bad example before, didn't have too much time.
This is how you could access the user, if your exam model has one.
from django.contrib.auth import get_user_model
class Exam(BaseModel):
...
STATE_CHOICES = (
(PASS, PASS),
(FAILED, FAILED),
(GREAT, GREAT),
state = models.CharField(max_length=15, choices=STATE_CHOICES, default=PASS)
user = models.ForeignKey(get_user_model(), on_delete=models.RESTRICT)
...
def save(self, *args, **kwargs):
if self.user.is_admin or self.user.is_superuser:
... your logic here
super(Exam,self).save(*args,**kwargs)
To access the request in the create/save method:
Pass it into the kwargs of the create/save method, of the form you want.
Then get the request in the create/save method, and do your logic
request = kwargs.get('request',None)
Edit, to get the request into the model's .save()
Django admin's save model function literally just calls obj.save()
So if you pass request=request into save like so:
def save_model(self, request, obj, form, change):
"""
Given a model instance save it to the database.
"""
obj.save(request=request)
it should work.
Override Admin save:
Override save method of Django Admin
I am new to DRF. I want to get saved the model.
In models.py, PackageDetails and PhysicalDetail have foreignkey relationship to Member
My serializers.py is as follows:
from rest_framework import serializers
from .models import Member, PackageDetails, PhysicalDetail
class PackageDetailsSerializer(serializers.ModelSerializer):
is_expired = serializers.SerializerMethodField()
members_expiry_date = serializers.SerializerMethodField()
class Meta:
model = PackageDetails
exclude = ['id']
extra_fields = ['is_expired', 'members_expiry_date']
def get_is_expired(self, instance):
return instance.is_expired
def get_members_expiry_date(self, instance):
return instance.members_expiry_date
class PhysicalDetailSerializer(serializers.ModelSerializer):
class Meta:
model = PhysicalDetail
exclude = ['id']
class MemberSerializer(serializers.ModelSerializer):
physical_details = PhysicalDetailSerializer(many=True)
package_details = PackageDetailsSerializer(many=True)
class Meta:
model = Member
fields = '__all__'
extra_fields = ['physical_details', 'package_details']
def create(self, validated_data):
physical_detail_data = validated_data.pop("physical_details")
package_detail_data = validated_data.pop("package_details")
member = Member.objects.create(**validated_data)
PhysicalDetail.objects.create(member=member, **physical_detail_data)
PackageDetails.objects.create(member=member, **package_detail_data)
return member
views.py :
class MemberViewset(viewsets.ModelViewSet):
queryset = Member.objects.all()
serializer_class = MemberSerializer
class PackageDetailViewset(viewsets.ModelViewSet):
queryset = PackageDetails.objects.all()
serializer_class = PackageDetailsSerializer
class PhysicalDetailViewset(viewsets.ModelViewSet):
queryset = PhysicalDetail.objects.all()
serializer_class = PhysicalDetailSerializer
In GET request it worked well.. but in POST request with the same json format it responses the following:
{
"physical_details": [
"This field is required."
],
"package_details": [
"This field is required."
]
}
I've provided the fields.. so why this happening..
You removed those from dict using pop()
The pop() method removes and returns an element from a dictionary having the given key.
Try using get() instead
The get() method returns the value for the specified key if the key is in the dictionary.
I want to change select option's label by user status.
my model is
class BookCategory(model):
label_1 = char
label_2 = char
def __unicode__(self):
#something here?
class Book(model):
name = char
categoey = models.Foreignkey(BookCategory)
BookCategory is used in createview for new book, and the page has modelform, textinput for book.name and choices for book.catgory.
My goal is
if user type==1:
=>display category's label_1
if user type==2:
=>display category's label_2
I know "__unicode__" can display instance's value, but I want to know change its field by user's status.
Anyone knows the solution?
add:
my view and modelform are so simple.
view is
class CreateBook(CreateView):
template_name = "template.html"
model = Book
form_class = BookForm
success_url = "success_url"
def form_valid(self, form):
if form.is_valid():
form.save()
and form is
class LessonForm(ModelForm):
class Meta:
model = Book
fields = ('name',
'category',
)
widgets = {
'name': forms.Textarea(attrs={'class': 'text_box'}),
'category': forms.Textinput(attrs={'class': 'select_box'}),
}
Subclass ModelChoiceField to create two custom models fields. Override label_from_instance to customize how you want the object to be displayed in the form.
class Type1CategoryField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.label1
class Type2CategoryField(ModelChoiceField):
def label_from_instance(self, obj):
return obj.label1
Then change your form so that it takes user as a keyword argument, and change the category field to use the appropriate model choice field.
class LessonForm(ModelForm):
class Meta:
model = Book
fields = ('name',
'category',
)
widgets = {
'name': forms.Textarea(attrs={'class': 'text_box'}),
}
def __init__(self, *args, **kwargs):
user = kwargs.pop('user')
super(LessonForm, self).__init__(*args, **kwargs)
if user.user_type == 1:
self.fields['category'] = Type1CategoryField(queryset=Category.objects.all())
elif user.user_type == 2:
self.fields['category'] = Type2CategoryField(queryset=Category.objects.all())
It doesn't make sense to use forms.Textinput for a choice field, so I removed category from your widgets.
Finally, modify your view so that it includes the logged-in user in the form kwargs. Use LoginRequiredMixin to make sure that only logged-in users can access the view.
from django.contrib.auth.mixins import LoginRequiredMixin
class CreateBook(LoginRequiredMixin, CreateView):
template_name = "template.html"
model = Book
form_class = BookForm
success_url = "success_url"
def get_form_kwargs(self):
kwargs = super(CreateBook, self).get_form_kwargs()
kwargs['user'] = request.user
In the admin panel, I can add Persons to my CompleteClass model. There is a M2M relationship between CompleteClass and Person. But, my form doesn't work as it should. The pub_date will update, and I can save the head_count, but not the ModelMultipleChoiceField (persons) -- it will not save.
models.py
class Person(models.Model):
name = models.CharField(max_length=255)
persona_description = models.CharField(max_length=255)
def __str__(self):
return self.name
class CompleteClass(models.Model):
persons = models.ManyToManyField(Person)
class_name = models.CharField(max_length=255)
class_head_count = models.IntegerField()
class_pub_date = models.DateField()
def __str__(self):
return '%s %s' % (self.class_name, self.class_head_count)
def save_complete_class(self):
self.class_pub_date = timezone.now()
self.save()
class Meta:
ordering = ('class_pub_date',)
Here is views.py:
def class_new(request):
if request.method == "POST":
form = CompleteClassForm(request.POST)
if form.is_valid():
complete_class = form.save(commit=False)
complete_class.class_pub_date = timezone.now()
complete_class.save()
form.save_m2m()
return redirect('class_detail', pk=complete_class.pk)
else:
form = CompleteClassForm()
return render(request, 'app/class_edit.html', {'form': form})
and forms.py
class CompleteClassForm(forms.ModelForm):
class Meta:
model = CompleteClass
fields = ('class_name', 'class_head_count',)
def __init__(self, *args, **kwargs):
user = kwargs.pop('user', None)
super(CompleteClassForm, self).__init__(*args, **kwargs)
self.fields['class_persons']=forms.ModelMultipleChoiceField(queryset=Person.objects.all())
I've read through the documentation and used the save_m2m since i've set commit=false.
The POST data contains person data, but it's not being written to the database. I'm stumped. Please help!
Only fields named in the fields tuple are saved to the instance. You don't have your m2m field listed there.
You also define your modelchoicefield with a different name - class_persons instead of persons. In fact, there is no reason to define that field separately at all - you haven't changed any of the attributes from the defaults.
And once you've removed that definition, there is also no reason to override __init__, seeing as you never pass the user parameter nor do you use it anywhere in the form.
I have two models: Foo which carries an owner field and Bar which has a relation to Foo:
class Foo(models.Model):
owner = models.ForeignKey('auth.User')
name = models.CharField(max_length=20, null=True)
class Bar(models.Model):
foo = models.OneToOneField(Foo, related_name='bar')
[...]
I use HyperlinkedModelSerializer for representation:
class BarSerializer(serializers.HyperlinkedModelSerializer):
foo = serializers.HyperlinkedRelatedField(view_name='foo-detail', queryset=Foo.objects.all())
[...]
class Meta:
model = Bar
fields = ('foo', [...])
class FooSerializer(serializers.HyperlinkedModelSerializer):
owner = serializers.SlugRelatedField(read_only=True, slug_field='username')
bar = serializers.HyperlinkedRelatedField(view_name='bar-detail', read_only=True)
class Meta:
model = Foo
fields = ('name', 'bar', 'owner')
My views look like this:
class FooViewSet(viewsets.ModelViewSet):
queryset = Foo.objects.all()
serializer_class = FooSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwner,)
def get_queryset(self):
user = self.request.user
if not user.is_authenticated():
return Foo.objects.none()
if user.username == "admin":
return Foo.objects.all()
return Foo.objects.filter(owner=user)
def perform_create(self, serializer):
serializer.save(owner=self.request.user)
class BarViewSet(viewsets.ModelViewSet):
queryset = Bar.objects.all()
serializer_class = BarSerializer
permission_classes = (permissions.IsAuthenticatedOrReadOnly, IsOwner,)
def get_queryset(self):
user = self.request.user
if not user.is_authenticated():
return Bar.objects.none()
if user.username == "admin":
return Bar.objects.all()
return Bar.objects.filter(foo__owner=user)
I don't whant User A to be able to see User B's stuff and vice versa. That works pretty well so far with one exception:
User A created an instance of Foo but doesn't instantly create an instance of Bar linking to Foo. Now User B can guess the URL to User A's Foo instance and specify that when creating his instance of Bar.
At this point, User A gets an instance of Bar which he didn't create.
I'm new to Django and rest_framework, so I have no idea how to solve this. Can somebody get me on the right track? My first idea was to use the foo field in BarSerializer to filter Foos using the queryset. But I didn't figure out how to get access to the auth.User object from there.
You can access the request inside a Serializer if you include it in its context.
Then you can do field level validation inside the Bar serializer:
def validate_foo(self, val):
user = self.context['request'].user
try:
foo = Foo.objects.get(pk=val)
except Foo.DoesNotExist:
raise serializers.ValidationError("Some Error")
if foo.user is not user:
raise serializers.ValidationError("Some Error")
return value