How to use custom_module_form_user_register_form_alter to change properties of user profile fields? - drupal-8

In a custom module I am trying to hook into custom profile fields on the user registration form. In the custom_module.module file I am using custom_module_form_user_register_form_alter to use an ajax callback to autofill one profile field based on the content of another field. But I have not been able to hook into the profile fields on the user registration form. I am able to access the basic user fields like so:
function custom_module_form_user_register_form_alter(&$form, FormStateInterface $form_state, $form_id) {
$form['account']['mail']['#required'] = TRUE;
$form['account']['mail']['#title'] = 'Changed title';
The above works but I have tried several different methods but can't touch the profile fields with my code. I tried:
$form['account']['field_registration_code']['#title'] = "Change this";
and
$form['account']['survey_participants_profiles[0][entity][field_registration_code][0][value]']['#title'] = "Change this`";
and
$form['survey_participants_profiles[0][entity][field_registration_code][0][value]']['#title'] = "Change this";
and several variations of the above. How do I call the custom profile fields from hook_form_alter?

Here is what finally worked after hours of guesswork:
$form['survey_participants_profiles']['widget'][0]['entity']['field_registration_code']['widget'][0]['value']['#title'] = 'blurg';

Related

How to test CheckboxSelectMultiple in save

I have a form (ModelForm) in Django, where I am adding a field for users in the init method as so:
self.fields["users"] = forms.ModelMultipleChoiceField(
queryset=users, widget=forms.CheckboxSelectMultiple, required=False,label="Add Designer(s)"
)
In the save method how I can iterate over the queryset for this field, however, I do not know how I can test if the particular model has been selected/checked. Help, please.
EDIT:
Let's say that you have a form where you want to be able to add users to a certain project, I set the users field as above (also usedMultipleChoiceField) but my real question is how do you determine the state of those checkboxes (which users should be added)?
Managed to fix it using MultipleChoiceField instead of ModelMultipleChoiceField. Then populated the choices with existing event IDs and passed it to the template.
In forms:
choices = forms.MultipleChoiceField(widget = forms.CheckboxSelectMultiple())
In views:
form.fields['choices'].choices = [(x.eventID, "Event ID: " + x.eventID) for x in unapproved]
Had to change some of the logic for finding and editing Event objects too.
The Django documentation states that a ModelMultipleChoiceField normalizes to a QuerySet of model instances. That means in your example, it will only return the users that have been checked. If none have been checked, it will return an empty QuerySet.
If you are overriding your ModelForm save method, you could include something like this:
selected_users = self.cleaned_data.get('users')
for user in selected_users:
project_users.add(user)

How to set User-based form field choices in a django ModelForm (with django-allauth)

I'm using Django 1.8 and django-allauth. I have a simple form with a dropdown menu that allows my Users to choose a Character model.
I have a UserCharacter model that looks like this:
class UserCharacter(models.Model):
user = models.ForeignKey(User)
character = models.ForeignKey(Character)
is_default = models.BooleanField(default=False)
In my main Form I'm only displaying the "character" field, which appears as a dropdown menu of Character objects. So far, so good - users can select a Character and it is saved as a UserCharacter association.
What I'm trying to do is display all Character objects that don't already have a UserCharacter linked to the currently logged in User. I would normally use the limit_choices_to feature, like so:
character = models.ForeignKey(Character, limit_choices_to={'id__in': UserCharacter.objects.filter(user_id=[USER_ID]))
my problem is, I don't know how to get access to the current user object from the UserCharacter Model or from a ModelForm. I normally need access to the current request to get at the django-allauth logged in user object. normally in place of [USER_ID] is I would put
self.request.user.id
to get the current user's id from django-allauth, but I can't do that from the Model here. How would I go about changing the form choices in the ModelForm based on the current user object?
Thanks so much!
Since this needs the currently logged in user then you have to do it in the View where you override the form characters QuerySet based on your user.
form.character.queryset = Character.objects.filter(....)
You can also pass the request.user to the form and do the filter there in the constructor.

How to render a ForeignKey field as CharField in Django forms

My Model
class Collaborator(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL)
My Form:
class CollaboratorForm(forms.ModelForm):
user = forms.CharField(max_length=30, required=False)
class Meta:
model = Collaborator
fields = ('user',)
The template render the user input text as an autocomplete field loaded from User model.
The page shows the user input text field correctly, however when I go to edit a Collaborator the user input text field shows the user Id and I want to show the username instead.
You want to figure out first what the exact behavior you want is.
Let's say I'm user with id=1, username="Joel". When you produce your CollaboratorForm and it shows "Joel" (my username) on the form and you edit it to read "Jane", are you:
trying to edit my name to "Jane", so that id=1 now has a username=Jane ?
or trying to change the associated user to the user matching that username?
Once you know that, you can proceed.
I'd change the user = forms.CharField(..) to username = forms.CharField(..); that's what your really showing, after all.
In your view, when you create your form, pass it the initial values of {'username':username-of-user-that-is-currently-related}.
In your view, when you process the form, before you save it, use the cleaned_data['username'] to either (a) update the username of that user (if the first case above) or (b) get the ID of the new user and save that to the .user field (if the second case above).
Of course, if what you want is really at a higher-level "let people pick the user based on the username", you might be able to solve this differently and more easily--by not using a CharField at all, and using a ChoiceField instead, so you could show a drop down menu of users with the key=ID and value=name. Depending on how many users you have, though, that might not scale well.

GenericForeignKey and Admin in Django

Let's say I have a Post object that can contain Images, Videos, and other media types. I can use a GenericForeignKey to link them together. Something like:
class Post(models.Model):
title = models.CharField(...)
text = models.TextField(...)
class AudioMedia(models.Model):
...
class VideoMedia(models.Model):
...
class ImageMedia(models.Model):
...
class MediaObject(models.Model):
post = models.ForeignKey(Post)
order = models.IntegerField()
content_type_media = models.ForeignKey(
ContentType, limit_choices_to={
'model__in': (
'audiomedia',
'imagemedia',
'videomedia')
})
object_id_media = models.PositiveIntegerField()
obj = generic.GenericForeignKey('content_type_media', 'object_id_media')
Now I can easily create an admin interface, like:
class MediaObjectAdminInLine(admin.StackedInline):
model = MediaObject
ct_field = "content_type_media"
ct_fk_field = "object_id_media"
extra = 0
class PostAdmin(admin.ModelAdmin):
inlines = [MediaObjectAdminInLine]
Now the question :) In admin/, I can easily create a new Post. To the post, I can easily add more MediaObject. In the panel, I have a drop down menu to chose the type (audio, video, ...), but I have to manually enter the ID of the object I want to link with Post.
I have tried various extensions, including grappelli. Some provide the ability to lookup the ID of objects to link here. I want the ability to add objects here, eg, add an AudioMedia, a VideoMedia, an ImageMedia, depending on what I pick from the dropdown.
Any suggestions?
You'd need to quite a bit of work to get this going.
You're asking that the admin dynamically display a modelform, based on what model type you chose from a drop down.
Django's admin does not do that (nor do any known extensions to it).
To make this work, you'll have to:
Write a custom JavaScript event handler which captures the onchange of the model select drop down.
Then calls Django's admin and requests the inline modelform for that model.
Updates the current HTML page with that model form.
Then you'll need to intercept the parent model's modelform's save() method to figure out which child modelform it's dealing with, and correctly save it to the database.
Then you'll need to sort out how to get the parent model's modelform to correctly display the appropriate child model's modelform dependent on the model of the child.
Sound daunting? It is.
Here's an easier way:
Just have a single "Media" model. You'll have a few fields on the model that are only valid for one of your types (though there's plenty of crossover).
Name any fields that are specific to a single Media type with a prefix for that mediatype, i.e. image_size', orvideo_title`.
Attach a JavaScript handler to your ModelAdmin which selectively shows and hides fields based on a dropdown for the media type. Something like this:
class MediaAdmin(admin.ModelAdmin):
class Meta:
js = ["js/media-types.js",]
// media-type.js
(function($) {
$(document).ready(function(){
$('.module[id^=module] .row').hide();
$('.module[id^=module] .row.module').show();
$('.module[id^=module] .row.module select').each(function(){
if ($(this).val() != '')
{
var group = $(this).parent().parent().parent().parent();
var field = $(this).parent().parent().parent();
var mtype = $(this).val().toLowerCase();
if (mtype != '')
{
$('.row', group).not(field).slideUp('fast');
$('.row[class*="'+mtype+'"]', group).slideDown('fast');
$('.row[class*="all"]', group).slideDown('fast');
}
else
{
$('.row', group).not(field).slideUp('fast');
}
}
});
$('.module[id^=module] .row.module select').change(function(){
var group = $(this).parent().parent().parent().parent();
var field = $(this).parent().parent().parent();
var mtype = $(this).val().toLowerCase();
if (mtype != '')
{
$('.row', group).not(field).slideUp('fast');
$('.row[class*="'+mtype+'"]', group).slideDown('fast');
$('.row[class*="all"]', group).slideDown('fast');
}
else
{
$('.row', group).not(field).slideUp('fast');
}
});
});
})(django.jQuery);
django-admin-genericfk doesn't work with Django 1.9.
Other than that I only found the following module:
https://github.com/lexich/genericrelationview
which looks well maintained. Unfortunately, its JS code does not work well with how Django CMS sets up jQuery (noConflict jQuery), so it seems that it is not an option for me. But it should be fine if not used in Django CMS pages but the regular Django Admin.
I realize this is pretty old, but this is still the first result when searching for this.
django-admin-genericfk does exactly what you need.

Is there an option to prevent some values from being displayed in django autocomplete widget?

I am using django-autocomplete in my "edit user details view" for adding "friends"
django-autocomplete works perfect but displays also the "current user" (who is editing his profile) and the user "anonymous"
I want to exclude those two.
How can I accomplish that?
models.py
class Profile(UserenaLanguageBaseProfile):
friends = models.ManyToManyField(User,related_name='userfriends', blank=True, null=True)
forms.py
class EditProfileForm(forms.ModelForm):
class Meta:
widgets = {
'friends': MultipleAutocompleteWidget(Profile.friends),
}
It's not a complete answer, but you should do something along those lines:
autocomplete = AutocompleteView()
class ProfileAutocomplete(AutocompleteSettings):
queryset = Profile.objects.exclude(friends='anonymous')
autocomplete.register(Profile.friends, UserAutocomplete)
But this will not exclude the current user. To get that you will have override/extend the view method of your ProfileAutocomplete class. In this method you will need to get the user ID (probably from the session) and then exclude it from the queryset. If using the session is not working (it's possible, I didn't put too much time on it), you 'll probably have to modify the jquery_autocomplete.js script to pass the user to the view method.