Django: add choices to a ForeignKey field - python

I'm trying to add a choices option to a ForeignKey field. But that prevents the input form from getting validated.
I'm not sure if you can use choices in ForeignKey field, but how else would you provide human readable output for the choices?
I don't want the user to be able to choose from all groups and don't want to display the actual group name as it contains additional info for the admins, that shouldn't be visible to the user, for cosmetic reasons.
Any idea how to achieve that would be greatly appreciated.
Here is my code:
The model:
from django.contrib.auth.models import Group as DjangoGroup
class Category(models.Model):
created_by = models.ForeignKey('auth.User', on_delete=models.PROTECT, related_name='category_author')
last_edited_by = models.ForeignKey('auth.User', on_delete=models.PROTECT, related_name='category_editor')
name = models.CharField(max_length = 100)
privileges = models.ForeignKey('auth.Group', on_delete=models.PROTECT, blank=True, null=True, choices=((DjangoGroup.objects.get(pk=5), 'Test group'),))
The Form:
from django import forms
from .models import *
class CategoryForm(forms.ModelForm):
class Meta:
model = Category
exclude = ('created_by', 'last_edited_by',)
With this code the form gives the desired option 'Test group', but when I try to submit it I get the following error:
Select a valid choice. TestGroup(just for testing purposes) is not one of the available choices.
with 'TestGroup(just for testing purposes)' being the actual group name that I am trying to hide from the user.
I hope you can understand what I am trying to achieve. Am I on the right track or is there a different way to do this?
Thanks in advance for any answer

Related

How to save two related entries in a single form

I am new to Django and I am creating a simple 2 page messageboard app (submit page and messageboard page)
I am struggling with the form for my submit page. As I am learning my way around Django I decided not to use the standard user model and opted to rather create a model (Poster) which has a one to one relationship with the message model.
Basically in one form I would like to add a message and a poster(foreign key) which has multiple fields.
Is it possible to achieve what I am trying to do?
Thanks in advance for the help.
I don't really know what to try or what to look for. I have included some code below.
Models
class Poster(models.Model):
full_name = models.CharField(max_length = 50)
phone_number = models.CharField(max_length = 15)
email = models.EmailField()
class Message(models.Model):
message_text = models.CharField(max_length=10000)
created_at = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey(Poster, on_delete=models.CASCADE)
class MessageForm(forms.ModelForm):
class Meta:
model = Message
fields = ['full_name', 'phone_number', 'email', 'message_text']
Your mistake is trying to use a ModelForm subclass which is for creating or updating one object (database row) only.
Use a plain forms.Form with the fields you want. You'll have to explicitly code them as CharField, EMailField, etc. Then in form_valid (assuming your view is a FormView) you will do something like
poster = Poster()
poster.full_name = form.cleaned_data['full_name']
# ditto for phone_number and email
poster.save()
message = Message( user=poster,
message_text = form.cleaned_data['message_text'] )
message.save()

Trying to extend AbstractUser to create multiple user types in Django

So I have been searching all around the internet for a full example of how to user AbstractUser when u have at least 2 different models. Didn't find anything conclusive.. at least that would work on latest version of Django (2.0.1).
I have 2 models, teacher and student, and registration needs to be different. Besides username, email, name and surname, I need for example, for the student, to upload a profile picture, email, phone, student_ID. And for teacher, bio, academic title and website. Did I start good ? What is the right approach ?
class Profile(AbstractUser):
photo = models.ImageField(upload_to='students_images')
email = models.EmailField()
phone = models.CharField(max_length=15, )
class Student(Profile):
student_ID = models.CharField(unique=True, max_length=14,
validators=[RegexValidator(regex='^.{14}$',
message='The ID needs to be 14 characters long.')])
def __str__(self):
return self.name
class Teacher(Profile):
academic_title = models.CharField(max_length=30)
bio = models.TextField()
website = models.URLField(help_text="E.g.: https://www.example.com", blank=True)
Your goals can be accomplished using a 'Profile' pattern. You don't necessarily need to use a custom user model for this. But you need to have a single common model to for authentication; you can use the builtin django user for this or a custom class... Your Student and Teacher models should be OnetoOne relationships. This is the recommended solution per the documentation.
If you wish to store information related to User, you can use a OneToOneField to a model containing the fields for additional information. This one-to-one model is often called a profile model, as it might store non-auth related information about a site user.
In your case, you may do something like this:
class StudentProfile(models.Model):
user = models.OneToOneField('User', related_name='student_profile')
# additional fields for students
class TeacherProfile(models.Model):
user = models.OneToOneField('User', related_name='teacher_profile')
# additional fields for teachers
Then you can create your registration forms based on these profile models.
class StudentResistrationForm(forms.ModelForm):
class Meta:
model = StudentProfile
fields = (...)
class TeacherRegistrationForm(forms.ModelForm):
class Meta:
model = TeacherProfile
fields = (...)
You can create the user instance to which the profile is related to at the same time you create the profile. You might do this with formsets, for example.
add
class Meta:
abstract = True
to profile model
and change AbstractUser to models.Model

How can I allow user to be "sloppy" when entering a Django phone number?

I'm making a Django form to update users membership to a website. I want to be able to store phone numbers. I found django-phonenumber-field which is great. The only problem is that the form I created for the user to enter their phone number is too specific. If the user doesn't enter their number as "+99999999" then they get an input error. I would like for the user to be able to enter their number a variety of ways: 999-999-9999, 9-999-9999, (999)999-9999, etc. What's the best way to accomplish this?
My code:
models.py
from django.db import models
from phonenumber_field.modelfields import PhoneNumberField
class Member(models.Model):
"""defines a member for annual registration"""
name = models.CharField(max_length=255)
mailing_address = models.CharField(max_length=255)
home_phone = PhoneNumberField()
other_phone = PhoneNumberField(blank=True)
email = models.EmailField()
forms.py
from django import forms
from .models import Member
class MembershipForm(forms.ModelForm):
"""form for renewing membership"""
class Meta:
model = Member
fields = ('name',
'mailing_address',
'home_phone',
'other_phone',
'email',
)
Thank you for any help!
That field definition is a custom field in Django. You can create your own custom fields. I would recommend getting the code for the PhoneNumberField, which is open source, and subclassing it to you own field MyPhoneNumberField. Override the validation logic to be as you wish. See the details of how these things work at
https://docs.djangoproject.com/en/1.11/ref/forms/validation/

Django: allow user to add fields to model

I am just starting with Django and want to create a model for an application.
I find Djangos feature to
- automatically define validations and html widget types for forms according to the field type defined in the model and
- define a choice set for the field right in the model
very usefull and I want to make best use of it. Also, I want to make best use of the admin interface.
However, what if I want to allow the user of the application to add fields to the model? For example, consider a simple adress book. I want the user to be able to define additional atributes for all of his contacts in the admin settings, i.e. add a fax number field, so that a fax number can be added to all contacts.
from a relational DB perspective, I would have a table with atributes (PK: atr_ID, atr_name, atr_type) and an N:N relation between atributes and contacts with foreign keys from atributes and contacts - i.e. it would result in 3 tables in the DB. right?
but that way I cannot define the field types directly in the Django model. Now what is best practice here? How can I make use of Djangos functionality AND allow the user to add aditional/custom fields via the admin interface?
Thank you! :)
Best
Teconomix
i would suggest storing json as a string in the database, that way it can be as extendable as you want and the field list can go very long.
Edit:
If you are using other damn backends you can use Django-jsonfield. If you are using Postgres then it has a native jsonfield support for enhanced querying, etc.
Edit 2:
Using django mongodb connector can also help.
I've used this approach, first seen in django-payslip, to allow for extendable fields. This provides a structure for adding fields to models, from which you can allow users to add/edit through standard view procedures (no admin hacking necessary). This should be enough to get you started, and taking a look at django-payslip's source code (see the views) also provides view Mixins and forms as an example of how to render to users.
class YourModel(models.Model):
extra_fields = models.ManyToManyField(
'your_app.ExtraField',
verbose_name=_('Extra fields'),
blank=True, null=True,
)
class ExtraFieldType(models.Model):
"""
Model to create custom information holders.
:name: Name of the attribute.
:description: Description of the attribute.
:model: Can be set in order to allow the use of only one model.
:fixed_values: Can transform related exta fields into choices.
"""
name = models.CharField(
max_length=100,
verbose_name=_('Name'),
)
description = models.CharField(
max_length=100,
blank=True, null=True,
verbose_name=_('Description'),
)
model = models.CharField(
max_length=10,
choices=(
('YourModel', 'YourModel'),
('AnotherModel', 'AnotherModel'), # which models do you want to add extra fields to?
),
verbose_name=_('Model'),
blank=True, null=True,
)
fixed_values = models.BooleanField(
default=False,
verbose_name=_('Fixed values'),
)
class Meta:
ordering = ['name', ]
def __unicode__(self):
return '{0}'.format(self.name)
class ExtraField(models.Model):
"""
Model to create custom fields.
:field_type: Connection to the field type.
:value: Current value of this extra field.
"""
field_type = models.ForeignKey(
'your_app.ExtraFieldType',
verbose_name=_('Field type'),
related_name='extra_fields',
help_text=_('Only field types with fixed values can be chosen to add'
' global values.'),
)
value = models.CharField(
max_length=200,
verbose_name=_('Value'),
)
class Meta:
ordering = ['field_type__name', ]
def __unicode__(self):
return '{0} ({1}) - {2}'.format(
self.field_type, self.field_type.get_model_display() or 'general',
self.value)
You can use InlineModelAdmin objects. It should be something like:
#models.py
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=100)
class ContactType(models.Model):
name = models.CharField(max_length=100)
class Contact(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
contact_type = models.ForeignKey(ContactType, on_delete=models.CASCADE)
value = models.CharField(max_length=100)
#admin.py
from django.contrib import admin
class ContactInline(admin.TabularInline):
model = Contact
class PersonAdmin(admin.ModelAdmin):
inlines = [
ContactInline,
]
By the way... stackoverflow questions should contain some code. You should try to do something before asking a question.

Question about ModelMultipleChoiceField

It so happened that I had to use arrays of PostgreSQL. In Django models do not have native support for arrays, so I used django_arrayfields. But for display in the admin should I use for the field hoprizontal_filter IntegerArrayField.
models.py
class Group(models.Model):
name = models.TextField()
class User(models.Model):
name = models.TextField()
groups = IntegerArrayField()
admin.py
class GroupAdminForm(forms.ModelForm):
groups = forms.ModelMultipleChoiceField(
queryset=Group.objects.all(),
label=('Select groups'),
required=True,
widget=FilteredSelectMultiple(
('groups'),
False,
))
class UserAdmin(admin.ModelAdmin):
fields = ('groups',)
form = GroupAdminForm
As a result of this widget is displayed and works properly. But while maintaining writes can't adapt type 'QuerySet'.
Please HELP!!!
UPD:
Request information in the field POST variable groups has value which corresponds only to the last id of the selected group. Rather than an array as I expect.
Can't help without a proper error/traceback.
For your updated point, remember that you need to do request.POST.getlist(fieldname) if you're expecting multiple values.
so, first we need separate the thins:
admin.py
from .forms import GroupAdminForm
class UserAdmin(admin.ModelAdmin):
fields = ('groups',)
form = GroupAdminForm
forms.py
class GroupAdminForm(forms.ModelForm):
groups = forms.ModelMultipleChoiceField(
queryset=Group.objects.all(),
label=('Select groups'),
required=True,
widget=FilteredSelectMultiple(
('groups'),
False,
))
do this and post your trace error, it's hard to help without logs or error description.

Categories