I have some model for a user profile and I want to be able to store several types of information but with some deferences (like a home or work phone number) but I don't want to use a ForeignKey relation ...how would I do that?
something like:
class Profile(models.Model):
phone = ? list of some kind ?
class Phone(???):
TYPE_CHOICES = (
('H', 'Home'),
('W', 'Work'),
('F', 'Fax'),
)
type = models.CharField(max_length = 1, choices = TYPE_CHOICES)
number = models.CharField(max_length = 16)
private = models.BooleanField()
Thank you!
Edit: I didn't want to use a foreign key just because I originally wanted all information relating to a user to show up on the profile admin page. but... meh... that's not too critical
Why don't you want to use a foreign key? If you want to have multiple phone numbers, that's the way you need/must do it.
It's easy to work with Django and foreign keys, and you can easily add an inline model formset into the admin page to create/edit a user profile with plenty of phone numbers.
Your models should look like this:
class Profile(models.Model):
name = models.CharField(max_length=40, verbose_name="User name")
class Phone(models.Model):
TYPE_CHOICES = (
('H', 'Home'),
('W', 'Work'),
('F', 'Fax'),
)
profile = models.ForeignKey(Profile)
type = models.CharField(max_length = 1, choices = TYPE_CHOICES)
number = models.CharField(max_length = 16)
private = models.BooleanField()
Then, you could use something like this to easily add/edit multiple phone numbers per profile in one admin page.
In your example, you should do something like this (inside admin.py file, in your django app):
class PhoneInline(admin.TabularInline):
model = Phone
class ProfileAdmin(admin.ModelAdmin):
inlines = [
PhoneInline,
]
admin.site.register(Profile, ProfileAdmin)
Now, you can go to the admin interface and try to add a new profile. You will see that you can add multiple phones per profile. I hope it helps you...
Finally, i'll recommend you to take a tour on django's tutorials. You will understand a lot of things and you will get a good idea of how to work with it.
Related
This might be a simple Django question, but I hope I can get som advice. The following code in admin.py
class ExarbeteMomentInline(admin.TabularInline):
model = ExarbeteMoment
extra=0
class ExarbeteStudent(admin.TabularInline):
model = ExarbeteStudent
extra=0
class ExamensarbeteAdmin(admin.ModelAdmin):
list_display = ('title', 'start_date', 'end_date', 'company')
inlines = [ExarbeteMomentInline, ExarbeteStudent]
admin.site.register(Examensarbete,ExamensarbeteAdmin)
produces what I want, in the admin panel.
But I want a version of it outside the admin panel, so that regular users can enter data. How can I modify the code in a minimal way to get essentially the same page outside the admin region?
Thanks, in advance.
/ADDED/
My models are:
class Examensarbete(models.Model):
title = models.CharField(max_length=10000,default='',blank=True)
start_date = models.DateField('startdatum')
end_date = models.DateField('slutdatum',blank=True,default='',null=True)
company = models.CharField(max_length=200,default='',blank=True)
comment = models.TextField(max_length=1000,default='',blank=True)
kurskod = models.CharField(max_length=100,default='FMAM05')
year = models.CharField(max_length=5,blank=True,default='',null=True)
class ExarbeteMoment(models.Model):
exarbete = models.ForeignKey(Examensarbete,on_delete=models.CASCADE)
typ = models.CharField(max_length=100)
person = models.ForeignKey(Personal,on_delete=models.SET_NULL,null=True)
tim = models.FloatField(null=True)
class ExarbeteStudent(models.Model):
exarbete = models.ForeignKey(Examensarbete,on_delete=models.CASCADE)
first_name=models.CharField(max_length=100)
last_name=models.CharField(max_length=100)
pnr = models.CharField(max_length=13)
program = models.CharField(max_length=5)
kull = models.CharField(max_length=5)
where I have deleted str and Meta. I guess I should be able to solve the problem with the help below, but I can't still figure out how I get what I get in the admin panel, with Examensarbete above and then two subforms with ExarbeteMoment and ExarbeteStudent. And the 'add another item' feature.
Unfortunately I am new to Django, and find it particularly hard to work with forms and formsets. I am not quite sure why, because they should simplify things considerably.
I agree with the comment from Roman Kovalevsky - you will need to write the functions by yourself. Django however supports you with forms and formsets to do this job. As you did not post your models in the question i can only show you an example with some random named variables:
class ExarbeteStudent(forms.Form):
description = forms.CharField(widget=AdminTextareaWidget)
date1 = forms.DateField(widget=AdminDateWidget)
date2 = forms.DateField(widget=AdminDateWidget)
task_list = Task.objects.all()
then you create a formset for the inline models as following
ExarbeteStudentFormSet = forms.formset_factory(ExarbeteStudent,
extra=1,
max_num=60,
can_delete=True,
Here you find some more informations about forms and formsets
https://docs.djangoproject.com/en/2.0/topics/forms/formsets/
I'll make a quick resume so you can understand better the structure:
The Cars can be driven by different drivers and these drivers can get trophies. Trophies must be associated to the drivers and to the car they used.
class CarDriver(models.Model):
driver = models.ForeignKey('Driver', null=False)
car = models.ForeignKey('Car', null=False)
trophies = models.ManyToManyField('Trophy', blank=True)
class Driver(models.Model):
name = models.CharField(max_length=255)
class Car(models.Model):
name = models.CharField(max_length=255)
drivers = models.ManyToManyField(Driver, blank=True, through=CarDriver)
class Trophy(models.Model):
position = models.IntegerField(default=1)
I want to display the model Car in Django Admin but using a list of checkboxes to select the drivers, so the driver selection will be way faster than using inlines:
class CardDriverInline(admin.TabularInline):
model = CarDriver
class CarAdmin(admin.ModelAdmin):
inlines = [
CardDriverInline,
]
admin.site.register(Car, CarAdmin)
Is there a way to use checkboxes for multiple driver selection?
According to the Django docs, when you use a through argument to a ManyToManyField, the admin will not display a widget by default.
So, in this case you must use inlines, unfortunately.
However, if you don't use a through argument, you can simply have a ModelAdmin like this:
class CarAdmin(admin.ModelAdmin):
formfield_overrides = {
models.ManyToManyField: {'widget': CheckboxSelectMultiple},
}
This way you have the checkboxes, but you lose the trophies reference.
I know, it isn't a fair world. I thought it would exist another way, but the Django docs is clear about why you have to use inlines with the through argument.
I would like to create a view with a table that lists all changes (created/modified) that a user has made on/for any object.
The Django Admin site has similar functionality but this only works for objects created/altered in the admin.
All my models have, in addition to their specific fields, following general fields, that should be used for this purpose:
created_by = models.ForeignKey(User, verbose_name='Created by', related_name='%(class)s_created_items',)
modified_by = models.ForeignKey(User, verbose_name='Updated by', related_name='%(class)s_modified_items', null=True)
created = CreationDateTimeField(_('created'))
modified = ModificationDateTimeField(_('modified'))
I tried playing around with:
u = User.objects.get(pk=1)
u.myobject1_created_items.all()
u.myobject1_modified_items.all()
u.myobject2_created_items.all()
u.myobject2_modified_items.all()
... # repeat for >20 models
...and then grouping them together with itertool's chain(). But the result is not a QuerySet which makes it kind of non-Django and more difficult to handle.
I realize there are packages available that will do this for me, but is it possible to achieve what I want using the above models, without using external packages? The required fields (created_by/modified_by and their timefields) are in my database already anyway.
Any idea on the best way to handle this?
Django admin uses generic foreign keys to handle your case so you should probably do something like that. Let's take a look at how django admn does it (https://github.com/django/django/blob/master/django/contrib/admin/models.py):
class LogEntry(models.Model):
action_time = models.DateTimeField(_('action time'), auto_now=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL)
content_type = models.ForeignKey(ContentType, blank=True, null=True)
object_id = models.TextField(_('object id'), blank=True, null=True)
object_repr = models.CharField(_('object repr'), max_length=200)
action_flag = models.PositiveSmallIntegerField(_('action flag'))
change_message = models.TextField(_('change message'), blank=True)
So, you can add an additional model (LogEntry) that will hold a ForeignKey to the user that changed (added / modified) the object and a GenericForeignKey (https://docs.djangoproject.com/en/1.7/ref/contrib/contenttypes/#generic-relations) to the object that was modified.
Then, you can modify your views to add LogEntry objects when objects are modified. When you want to display all changes by a User, just do something like:
user = User.objects.get(pk=1)
changes = LogEntry.objects.filter(user=user)
# Now you can use changes for your requirement!
I've written a nice blog post about that (auditing objects in django) which could be useful: http://spapas.github.io/2015/01/21/django-model-auditing/#adding-simple-auditing-functionality-ourselves
One of my models contains a ForeignKey-field to a model that has multiple thousand instances.
When I display a record, all of these are loaded into a dropdown, which I a) don't need and b) is slow as frack, especially when displaying multiple records on one page.
Page size shoots up to multiples of 3.5mb because of the size of the dropdown.
I thought about using "limit_choices_to" to contain that, but
country = models.IntegerField(blank=True, null=True)
location = models.ForeignKey(Geonames, limit_choices_to = {'cowcode': country}, related_name='events')
does not work.
Is there even a way to do that?
Update:
What do I want to display?
I want to show all places (Geonames) that are in the country of the EventRecord that the code above is taken from. I want to show only these places, not the whole list of all possible places.
Why don't I need all places?
a) Page load times: 3.5 minutes for a page load is a tad too long
b) See above: An Event takes place in a certain country, so I don't need to show locations that are not in that country
What you want is to make limit_choices_to aware to your instance, which is not possible.
What you should do is set the queryset property of location field in your admin form, something similar to this:
class EventRecordAdminForm(forms.ModelForm):
class Meta:
model = EventRecord
def __init__(self, *args, **kwargs):
super(EventRecordAdminForm, self).__init__(*args, **kwargs)
self.fields['location'].queryset = Geonames.objects.filter(cowcode=self.instance.country)
and of course use that form for your admin:
class EventRecordAdmin(admin.ModelAdmin):
form = EventRecordAdminForm
See here for docs
HTH!
if you are using admin interface you can use raw_id_fields in ModelAdmin:
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'publisher', 'publication_date')
list_filter = ('publication_date',)
date_hierarchy = 'publication_date'
ordering = ('-publication_date',)
filter_horizontal = ('authors',)
raw_id_fields = ('publisher',)
from Django Book:
sometimes you don’t want to incur the overhead of having to select all the related objects to display in the drop-down. For example, if our book database grows to include thousands of publishers, the “Add book” form could take a while to load, because it would have to load every publisher for display in the box.
The way to fix this is to use an option called raw_id_fields. Set this to a tuple of ForeignKey field names, and those fields will be displayed in the admin with a simple text input box () instead of a select.
Not sure why that is not working for you. But I think a better solution would be to use django-smart-selects. That way you can have the user choose country first. Then the Geoname dropdown is only populated when the user first chooses country.
From the docs:
If you have the following model:
class Location(models.Model)
continent = models.ForeignKey(Continent)
country = models.ForeignKey(Country)
area = models.ForeignKey(Area)
city = models.CharField(max_length=50)
street = models.CharField(max_length=100)
And you want that if you select a continent only the countries are available that are located on this continent and the same for areas you can do the following:
from smart_selects.db_fields import ChainedForeignKey
class Location(models.Model)
continent = models.ForeignKey(Continent)
country = ChainedForeignKey(
Country,
chained_field="continent",
chained_model_field="continent",
show_all=False,
auto_choose=True
)
area = ChainedForeignKey(Area, chained_field="country", chained_model_field="country")
city = models.CharField(max_length=50)
street = models.CharField(max_length=100)
This example asumes that the Country Model has a continent = ForeignKey(Continent) field and that the Area model has country = ForeignKey(Country) field.
I am trying to create an intranet/extranet with internal/external user-specific profiles, with a common generic profile. I've looked at several answers on this site, but none specifically address what I'm looking to do. Below are the (stripped down) files I have so far.
What's the best way to create a profile model, with subprofiles for each user type? I'm trying not to require a custom authentication backend if at all possible.
https://gist.github.com/1196077
I have a solution I dont Know if its the best but see it:
models.py
from django.db import models
from django.contrib.auth.models import User
class Pollster(models.Model):
"""docstring for Polister"""
user = models.OneToOneField(User, related_name = 'polister', unique=True)
cedule = models.CharField( max_length = 100 )
class Respondent(models.Model):
""" """
born_date = models.DateField( verbose_name=u'fecha de nacimiento' )
cedule = models.CharField( max_length = 100, verbose_name=u'cedula' )
comunity = models.CharField( max_length = 100, verbose_name=u'comunidad')
phone = models.CharField( max_length = 50, verbose_name=u'telefono')
sanrelation = models.TextField( verbose_name =u'Relacion con SAN')
user = models.OneToOneField( User, related_name = 'respondent')
I create a MiddleWare: so
i create middleware.py
from django.contrib.auth.models import User
from encuestas.models import Pollster, Respondent
class RequestMiddleWare(object):
"""docstring for """
def process_request(self,request):
if isPollster(request.user):
request.user.userprofile = Pollster.objects.get( user = request.user.id)
elif isRespondent(request.user):
request.user.userprofile = Respondent.objects.get(user = request.user.id)
return None
def isPollster(user):
return Pollster.objects.filter(user=user.id).exists()
def isRespondent(user):
return Respondent.objects.filter(user=user.id).exists()
and you need to configure settings.py for the middleware:
add to MIDDLEWARE_CLASSES atribute:
'encuestas.middleware.RequestMiddleWare'
encuestas is my_app name
middleware is the Middleware file
RequestMiddleWare is the middleware class
You need a combination of storing additional information about users and model inheritance.
Basically, you'll need the generic User models we all know and either love or hate, and then you need a generic profile model that is your AUTH_PROFILE_MODULE setting.
That profile model will be a top-level model, with model subclasses for internal and extrernal users. You probably don't want an abstract model in this case since you'll need a common profile table to load user profiles from with User.get_profile().
So...I think the major thing you want to change is to make your Associate, External, etc. models inherit from your Profile model.
Please check this excellent article that describes how to inherit from the User class and add your own information. For me, at least, this clearly seems to be the way to go: http://scottbarnham.com/blog/2008/08/21/extending-the-django-user-model-with-inheritance/
Using this method one should easily be able to add multiple user types to their Django application.