django: Admin interface for an extended class - django

I have two classes for which I want an admin interface. But one class extends the other. Ex
class Questions(models.Model):
pass
class MathQuestion(Questions):
some fields ....
Now the simplest way is to create a separate admin for MathQuestion. However, is there a possibility that I can have MathQuestion displayed in a more intuitive fashio in Admin, ex: When user goes to QuestionAdmin interface and selects a further type for Maths.

Add extra field 'question_type' with default value of None and not nullable (so you can be sure Question can't have instances, not sure if it works)
overload the save method in child class and assign there some value to question_type, meaning MathQuestion.
Use filtering in admin as always.

Related

How to reuse existing admin models to display a custom Queryset?

I'm writing my first application in Django, and now that the base is covered, I try to enhance a bit the admin part to ease up my life.
I have two classes in my model:
class Puzzle(models.Model):
puzzle_pieces = models.ForeignKey(PuzzlePieces,on_delete=models.CASCADE)
class PuzzlePieces(models.Model):
puzzle_pieces = models.CharField(max_length=255, default='empty')
with admin models in place too:
class PuzzlePiecesAdmin(admin.ModelAdmin):
class PuzzleAdmin(admin.ModelAdmin):
I want to define in the PuzzlePiecesAdmin class a custom action to display (reusing the format defined in PuzzleAdmin) all the puzzles which are linked to the selected puzzle_pieces
I know how to create custom actions,
def show_related_puzzles(modeladmin, request, queryset):
I've seen on the internet different ways to filter directly within the PuzzleAdmin class,
but not how to set the queryset from the outside.
But I don't understand how to launch the display of an instance of PuzzleAdmin limited to the queryset I will define within show_related_puzzles.
Could anyone explain me how to proceed?
Thanks in advance
OK, I found the start of an answer in the following:
https://stackoverflow.com/a/1652377/12505071
the standard changelist view accepts normal queryset filter parameters as GET arguments. So you can do:
/admin/puzzles/puzzle/?puzzle_pieces__pk=21
I finally found out by trial and error how to add another filter:
/admin/puzzles/puzzle/?difficulty__id__exact=35&puzzle__pk=560
Could anybody tell me how to add a second value for the same parameter?

Is there a way to add more than one model to a class based view?

Is there a way to add more than one model to a class based view ?!
This is my detail view.
class ArticleDetailView(DetailView):
model = Article
I want to add another model to my view .
I know i can use function based views in order to to have multiple models in my view.
But i'm wonder that is there any way i can do this with my class based view ?
Yes, but you have to handle excess models yourself. If you want to show details of two separate models in one view, you will need to override get_context_data. Of course you will need to write your custom methods to fetch the object.
If it is ambiguous to determine which model is main model (meaning that the majority of the view revolves around it and the other models are just supplementary, for example: a user profile needs user model and may need other models such as posts or favorite post etc., In this case the main model is user model), you should really use TemplateView to handle all of them yourself, instead of DetailView or ListView.
Check out this site, which has plenty of info to see what methods does class methods have and how to override them.

How do I prevent clients from creating an instance of the model class via the default init method?

One of the properties in the model class is a generated value.
Thus I want to limit the user from inserting their own opaque string.
I want the user to use one of my manager methods to create an instance.
Is there a way to prevent the user in my use case ?
One way of doing it would be to "hide" the value by actually giving the field a different name, and only exposing it via a property:
class MyClass(models.Model):
_real_field = models.CharField(max_length=10)
#property
def fake_field(self):
return self._real_field
This way, you can set real_field in your Manager, but only document the existence of fake_field.
The downside of this is that you would have to use _real_field in your queries.

Django: Want a ManyRelatedManager, getting ManyToManyField instead

So in my project I am trying to extend the User model to a Staff class and the Group model to a PermGroup class. However, when I save a PermGroup in the Staff's groups field (inherited from User) it only saves the PermGroup object as a Group and all of the fields and methods I defined in my PermGroup class are stripped away. So I decided the best course of action would be to override the groups field. From an earlier stackoverflow question I found and Django documentation, this should work.
class Staff(User):
User.groups = models.ManyToManyField('PermGroup', blank=True)
I need to use 'PermGroup' because the class shows up later in the file, and PermGroup has a field that relies on the Staff class, so if i switched the order I would have the same problem, only in the PermGroup class.
Now the problem I am having is that groups is now a ManyToManyField object where all the other "manytomany" fields are ManyRelatedManagers. I want groups to be a ManyRelatedManager but I do not know how.
Is it possible to get groups to be a ManyRelatedManager when I initiatize it using the 'PermGroup' model call?
If my approach is wrong and you can suggest an alternative to saving PermGroups in the Staff class. I would greatly appreciate it.
Why not just have your Staff be a standard model with a ForeignKey (OneToOneField, to be more exact) to his/her corresponding User?
And, to remove the circular dependency problem, you just need to make one dependent on the other. For instance, the PermGroup model could have a field of a ManytoMany of the Staff members in that group. There's no need for Staff to have a PermGroup, because if you wanted to see what groups a member belongs to, you'd just do something like this:
groups_theyre_in = PermGroups.objects.filter(staff_members__id=id_were_looking_for)

How do you modify the default widget for all builtin form fields of a certain type in Django?

This is a follow-up on How do you change the default widget for all Django date fields in a ModelForm?.
Suppose you have a very large number of models (e.g. A-ZZZ) that is growing with the input of other developers that are beyond your control, and you want to change the way all date fields are entered (i.e. by using jQueryUI). What's the best way to ensure that all date fields are filled out using that new widget?
One suggestion from the cited question was:
def make_custom_datefield(f):
if isinstance(f, models.DateField):
# return form field with your custom widget here...
else:
return f.formfield()
class SomeForm(forms.ModelForm):
formfield_callback = make_custom_datefield
class Meta:
# normal modelform stuff here...
However, is this possible to do where you don't have explicit ModelForm's, but url patterns come from models directly? i.e. your url config is likeso:
url(r'^A/?$', 'list_detail.object_list', SomeModelA)
where SomeModelA is a model (not a form) that's turned into a ModelForm by Django in the background.
At present in my system there are no Forms for each Model. The only point of creating forms explicitly would be to add the formfield_callback suggested in the prior solution, but that goes against DRY principles, and would be error prone and labour intensive.
I've considered (as suggested in the last thread) creating my own field that has a special widget and using that instead of the builtin. It's not so labour intensive, but it could be subject to errors (nothing a good grep couldn't fix, though).
Suggestions and thoughts are appreciated.
It sounds like you want to do this project-wide (ie: you're not trying to do this in some cases, but in ALL cases in your running application).
One possibility is to replace the widget attribute of the DateField class itself. You would need to do this in some central location... something that is guaranteed to be loaded by every running instance of the django app. Middleware can help with this. Otherwise, just put it in the __init__ file of your app.
What you want to do is re-assign the widget property for the forms.DateField class itself. When a new DateField is created, Django checks to see if the code specifies any particular widget in the field property definition. If not, it uses the default for DateField. I'm assuming that if a user in your scenario really defined a particular widget, you'd want to honour that despite the change to your global API.
Try this as an example of forcing the default to some other widget... in this case a HiddenInput:
from django import forms
forms.DateField.widget = forms.HiddenInput
class Foo(forms.Form):
a = forms.DateField()
f = Foo()
print f.fields['a'].widget
# results in <django.forms.widgets.HiddenInput object at 0x16bd910>