there are plenty of examples the tell you how to extend the user model BUT I cannot find a real, complete and documented example on how to extend an existing model without having to follow the "user profile pattern" (and honestly I wonder why).
In short, my use case is the following: I need to extend django-lfs's product model.
In LFS is registered like this (in lfs.catalog.admin):
from django.contrib import admin
[...]
from lfs.catalog.models import Product
[...]
class ProductAdmin(admin.ModelAdmin):
prepopulated_fields = {"slug": ("name", )}
admin.site.register(Product, ProductAdmin)
[...]
I tried to register mine (that subclasses it) but I got:
django/contrib/admin/sites.py",
line 78, in register
raise AlreadyRegistered('The model %s is already registered' %
model.name)
So, someone suggested me that I have to unregister that object and register mine.
I did it like this:
from lfs.catalog.models import Product
from lfs.catalog.admin import ProductAdmin
admin.site.unregister(Product)
from lfs_product_highlights.catalog.models import Product
admin.site.register(Product,ProductAdmin)
No errors this time BUT there's no change, my custom fields are nowhere to be seen.
Any hints?
The reason why it's difficult is because of the object-relational impedance mismatch (love that phrase). Objects and classes do not map perfectly onto relational databases: ORMs like Django's attempt to smooth out the edges, but there are some places where the differences are just too great. Inheritance is one of these: there is simply no way to make one table "inherit" from another, so it has to be simulated via Foreign Keys or the like.
Anyway, for your actual problem, I can't really see what's going on but one possible way to fix it would be to subclass ProductAdmin as well, and specifically set the model attribute to your subclassed model.
Related
I created a project and within the project an app call myusers. I then created a model with the class name AllUsers. I then populated the model with data from faker.
When I go to 127.0.0.1/admin under authorization I have groups and users. Under myusers I have ‘All userss’ which is a link to http://127.0.0.1:8000/admin/myusers/allusers/
so, I’m just wondering if this is a minor bug. Shouldn’t it say ‘AllUsers’ and not ‘All userss’? Or did I corrupt something along the way?
No, a model normally has a singular name, so AllUser, not AllUsers.
Django will, based on the name add a suffix …s for the plural. Indeed, for a model you can check the verbose name with:
>>> from django.contrib.auth.models import User
>>> User.Meta.verbose_name_plural
'users'
For your model you can specify the singluar and plural name yourself with:
class AllUsers(models.Model):
# …
class Meta:
verbose_name = 'all user'
verbose_name_plural = 'all users'
But nevertheless, it is better to give the class a singular name. Especially since other parts, like the default for related_name=… [Django-doc] is modelname_set, so for a ForeignKey of your AllUser model, that would look like allusers_set, which does not look very pleasant.
You should not create a model that represents a set of entities. A model should represent a single entity. Since a model should only represent a single entity, Django adds an "s" to the end in the admin to pluralize it (i.e, a model named "Car" will be "Cars" in the admin).
Of course you can change the verbose_plural_name (https://docs.djangoproject.com/en/3.1/ref/models/options/#verbose-name-plural), but the best way forward is to not continue using this technique to represent all users at all.
To represent all users you should be using a QuerySet.
Up until recently, a project I'm working used one mega UserProfile to handle all profile data for two different types of users. Naturally this was messy, and it was about time to refactor it.
In my attempt to refactor the model, I split the model into Requester and Funder and created an abstract UserProfile model which both subclass:
class UserProfile(models.Model):
class Meta:
abstract = True
user = models.OneToOneField(User)
def __unicode__(self):
return unicode(self.user)
class Requester(UserProfile):
def requested(self, event):
"""Check if a user requested an event."""
return self == event.requester
class Funder(UserProfile):
osa_email = models.EmailField(null=True) # The e-mail of the contact in OSA
mission_statement = models.TextField(max_length=256)
And in my settings.py file, I adjusted the AUTH_PROFILE_MODULE.
AUTH_PROFILE_MODULE = "app.UserProfile"
The problem is, when hitting a page that uses "User.get_profile()" it breaks, reporting:
Unable to load the profile model, check AUTH_PROFILE_MODULE in your project settings
I'm not quite sure what's going on here. According to the docs, everything looks right.
Can some explain why this fails? (There are a bunch of alternative solutions I've come across, but I'd much prefer to fix this if possible than adopt some hack.)
What you are trying to do it not possible. AUTH_PROFILE_MODULE is expecting a concrete model, not an abstract one. Concrete means it has a table and can create instances. An abstract model can only be subclassed.
A logic reason why this not possible it that django has no one of knowing which model instance to return for your user. A Requester? A Funder? Simply being an abstract reference gives django no hints. One approach might be to look into the contenttypes framework and maybe come up with a generic UserProfile model containing a reference to the proper sub-profile type. You could then remove the abstract=True from your UserProfile, and create a generic relation to the specific Profile model. AUTH_PROFILE_MODULE would then simply reference that single UserProfile, but its instances can then use the .content_object to get the specific subobject.
There are many ways I'm sure you could address this problem, but I am just commenting on the reason why this specific approach does not work.
I have dozens of Models, each with ONE associated ModelForm (whose Meta.model refers to the Model in question).
E.g.
class FooModel(Model):
pass
class FooModelForm(ModelForm):
class Meta:
model = FooModel
# current approach using a classmethod
FooModelForm.insert_in_model() # does cls.Meta.model.form = cls
So, obviously, it's easy to find FooModel given FooModelForm. What I want is to know the best way to do the REVERSE: find FooModelForm when I am presented with FooModel or even the string "Foo".
Assume only one ModelForm for each model, although solutions that return multiple are fine.
My current approach is to stash the model in the form class (as shown above), but I'm interested in knowing better approaches especially ones that could compute it centrally (without the final line above).
EDIT: I've reviewed things like Django: Display Generic ModelForm or predefined form but I believe this is a simpler question than those. The Django admin code must do something along the lines of what I seek. But get_model equivalent for ModelForms? suggests that might be voodoo and that it would be best to just do dict['Foo']=FooModelForm or its equivalent to keep track of the association explicitly. Seems repetitious.
If you have under 20 forms, sounds like mapping out a dictionary is the easiest way. Django does this kinda thing internally too.
For ModelForms, django admin just creates them on the fly via modelform_factory, so there is no comparable method to get_model
I do see, your method is bullet proof, but requires a line in ever model def.
If you only have one ModelForm per model, you could potentially iterate through the ModelForm subclasses until you find your form.
find FooModelForm when I am presented
with FooModel or even the string
"Foo".
modelforms = forms.ModelForm.__subclasses__()
def get_modelform(model):
try:
return filter(lambda x:x.Meta.model == model, modelforms)[0]
except IndexError:
print "apparently, there wasn't a ModelForm for your model"
If you want to pull the ModelForm as a string, you'll need to make sure both
app_label and __name__ are correct, which means it will be easier to use get_model('app', 'model') in the function.
You could combine this with your method and automatically place an attribute on your models that point to its ModelForm.
Hook into the class_prepared signal at the top of your apps, find the corresponding ModelForm and attach it to your Model class.
Hope that helps or gives you some ideas.
I'm using Django 1.2 and I want to have two user types (one for companies and one for consultants). I will either use an object in my model (something like a boolean for is_company or is_consultant) or Django's groups to distinguish them--depending on which is easier for this problem. I guess it wouldn't be much of a problem if I weren't a total noob ;)
I'm using django-registration for my authentication backend, and I will have a separate form on my webpage for each user type (company vs consultant). I don't think it is best to create two different views that are almost identical for the two cases, so I'm wondering what the best way is to identify/register the users who signed up as either of the two types.
Thanks for your help.
Do you want the user to pick if they are a consultant or company when registering? If so, you can create your own form by subclassing the RegistrationForm and then passing your new form into the parameters for django-registration (Read the doc on how to do that.)
To subclass the form and add the additional field you would do something like so:
from registration.forms import RegistrationForm
USER_TYPES = (
('consultant', 'Consultant'),
('company', 'Company'),
)
class MyRegistrationForm(RegistrationForm):
user_type = forms.ChoiceField(choices=USER_TYPES)
From then, you should catch the signal and do as you need with the form data django-registration has great documentation
Hope that's what you were lookign for.
Rather than looking in the POST, you can pass the information in the query string.
So one "button" (which is really just a link) links to /form?type=consultant, and the other links to /form?type=company and then you can grab it from the GET information
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>