Suppose I have a following code:
File models.py:
from django.db import models
from django.contrib.auth.models import User
class MyClass(models.Model):
username = models.ForeignKey(User, blank=True, null=True)
my_field = models.CharField(max_length=200, default="sample_field")
File views.py
from django.forms.models import inlineformset_factory
from django.contrib.auth.models import User
from myapp.models import MyClass
#login_required
def index(request):
username = User.objects.get(username=request.user.username)
MyClassFormSet = inlineformset_factory(User, MyClass, can_delete=False, extra=5)
formset = MyClassFormSet(instance=username)
...
What is the easiest way to add CSS class to the field my_field here?
(I saw some answers on SO for forms, but not for models).
Create a form from the model and define UI attributes there, that is the correct place to do it e.g.
class MyForm(ModelForm):
class Meta:
model = MyClass
fields = ('my_field')
widgets = {
'my_field': TextInput(attrs={'class': 'mycssclass'}),
}
That should set correct class for your field, then in HTML file set the needed css attributes e.g.
.mycssclass {
color: red;
}
If you are using inlineformset_factory you can still pass a widgets dict to it, where widgets is a dictionary of model field names mapped to a widget, or you can pass a custom form to it, so you can do something like this
MyClassFormSet = inlineformset_factory(User, MyClass, form=MyForm, can_delete=False, extra=5)
Related
In my django app I want to set focus to the first CharField (task) when the page loads.
my models.py is
from django.db import models
class ListModel(models.Model):
task = models.CharField(max_length=255)
status = models.BooleanField(default=False)
def __str__(self):
return f"{self.task} : {str(self.status)}"
and forms.py is
from django.forms import ModelForm
from .models import ListModel
class ListForm(ModelForm):
class Meta:
model = ListModel
fields = ["task", "status"]
I have tried adding the following widget in my CharField (in models.py):
task = models.CharField(max_length=255, widget=models.TextInput(attrs={'autofocus': True})
but it gives an AttributeError: module 'django.db.models' has no attribute 'TextInput'
I have also tried adding the following to the ListForm class (in forms.py):
def __init__(self):
self.fields['task'].widget.attrs.update(autofocus = 'autofocus')
though I am not getting any error for this, but when I load my page the focus is not set to the task CharField either. What can I do add auto-focus to my CharField?
You are confusing model fields (which are used to store data in the database), and form fields, which are used to obtain, validate and clean data the user has entered.
You thus work with:
from django.forms import ModelForm
from django import forms
from .models import ListModel
class ListForm(ModelForm):
# forms ↓
task = forms.CharField(
max_length=255,
# forms ↓
widget=forms.TextInput(attrs={'autofocus': True})
)
class Meta:
model = ListModel
fields = ['task', 'status']
I am using a ready module "account" and I want to supplement it with the help of inheritance with another attribute "photo". In models.py I create class Account, that inherits from Account from "account" module.
extended_account/models.py
from django.db import models
from account.models import Account
class Account(Account):
photo = models.URLField(blank=True, default="default")
But in views.py I have no idea how to chains my own Account model with PhotoView. self.request.user.account still refers to the Account from "account" model and has no "photo" attribute.
How do i make self.request.user.account refer to the new Account in extended_account/models.py?
extended_account/views.py
from django.views.generic.edit import FormView
from account.mixins import LoginRequiredMixin
from extended_account.forms import PhotoForm
class PhotoView(LoginRequiredMixin, FormView):
template_name = "account/new_photo.html"
form_class = PhotoForm
def get_initial(self):
initial = super(PhotoView, self).get_initial()
initial["photo"] = self.request.user.account.photo
return initial
def update_account(self, form):
fields = {}
if "photo" in form.cleaned_data:
fields["photo"] = form.cleaned_data["photo"]
if fields:
account = self.request.user.account
for k, v in fields.items():
setattr(account, k, v)
account.save()
extended_account/forms.py
class PhotoForm(forms.Form):
photo = forms.URLField(required=False)
First of all, I'd rename the new account model, so you can have a clear difference between the two.
Then, you can use a ForeignKey instead of inheriting the class. It'd look like this:
from django.db import models
from account.models import Account
class ExtendedAccount(models.Model):
account = models.ForeignKey(Account,
on_delete=models.CASCADE,
related_name="extended_data")
photo = models.URLField(blank=True, default="default")
# If you want to add more fields to the account model, do it here
Once you have this class, you can create a ModelForm for this class:
from django.forms import ModelForm
from .models import ExtendedAccount
class ArticleForm(ModelForm):
class Meta:
model = ExtendedAccount
fields = ['photo']
Now, if you want to access the photo of the model, just do it as a normal ForeignKey:
a = Account() # Complete the required fields
a.extended_data.photo
I have a ModelForm, which I'm trying to have a dynamic select in it.
My ModelForm in forms.py:
class AuxiForm(forms.ModelForm):
class Meta:
model = Auxi
fields = ["tipAux"]
widgets = {
'tipAux': forms.Select(attrs={'class': 'form-control', 'placeholder': 'Tipo'}),
}
labels = {
'tipAux': 'Tipo',
}
and I want to have a choicefield which should be dynamic, filling itself by a query from an other class called TipoAux.
TipoAux in models.py:
class TipoAux(models.Model):
denom = models.CharField(max_length=30, null=True)
def __str__(self): # Python 3
return self.denom
Conclusion: I'm my form I should have a dynamic select which collects its options from TipoAux class
Like this:
Options = (
(1, 'First option',
(2, 'Second option',
)
But getting its options from my DB, and not having to add them manually.
To have this structure you should follow and do the next steps:
Create a Model called TipoAux:
class TipoAux(models.Model):
denom = models.CharField(max_length=50)
def __str__(self):
return self.name
Then immediately run migrate since the other table will depend on this (if you do not have this table yet).
Then create the other things like the other Model (this is what you are actually most interested in with your question):
class Auxi(models.Model):
# we get the TipoAux choice values from the TipoAux table and creating a list of that
all_tipoaux = TipoAux.objects.values()
TIPAUX_CHOICES = [(d['id'], d['denom']) for d in all_tipoaux]
tipAux = models.IntegerField(choices=TIPAUX_CHOICES, null=True, verbose_name='Tipo')
Then your Form (first just make it simple, do not use select widget and label yet, since it’s automatically created due to the model):
from .models import Auxi
class AuxiForm(forms.ModelForm):
class Meta:
model = Auxi
fields = ["tipAux"]
Then your view something like this:
from django.shortcuts import render, redirect
from django.http import HttpResponseRedirect, HttpResponse, HttpRequest
from django.urls import reverse
from .forms import AuxiForm
from .models import Auxi
def tipo(request):
if request.method == 'POST':
form = AuxiForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(reverse('myappname:index'))
else:
form = AuxiForm()
return render(request, 'myappname/auxiform.html', {'form': form})
You have to run migration again to create the Auxi table
Then you will just create a url path to the view in urls.py and do not forget to register your models in the admin.py.
from .models import TipoAux, Auxi
admin.site.register(TipoAux)
admin.site.register(Auxi)
Then you have to go to your admin page of your site and create some items in the TipoAux table for having some option values.
And this is the visual end result of the above (recorded my results in gif):
I hope this will be in help of you. Cheers. ;)
There are 150k entries in User model. When i am using it in django-admin without the raw_id_fields it is causing problem while loading all the entries as a select menu of foreign key. is there alternate way so that it could be loaded easily or could become searchable?
I have these models as of defined above and there is a User model which is used as ForeignKey in ProfileRecommendation models. The database entry for user model consist of around 150k entries. I don't want default select option for these foreign fields. Instead if can filter them out and load only few entries of the user table. How I can make them searchable like autocomplete suggestion?
admin.py
class ProfileRecommendationAdmin(admin.ModelAdmin):
list_display = ('user', 'recommended_by', 'recommended_text')
raw_id_fields = ("user", 'recommended_by')
search_fields = ['user__username', 'recommended_by__username',]
admin.site.register(ProfileRecommendation, ProfileRecommendationAdmin)
models.py
class ProfileRecommendation(models.Model):
user = models.ForeignKey(User, related_name='recommendations')
recommended_by = models.ForeignKey(User, related_name='recommended')
recommended_on = models.DateTimeField(auto_now_add=True, null=True)
recommended_text = models.TextField(default='')
you can use method formfield_for_foreignkey
something like this:
class ProfileRecommendationAdmin(admin.ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == "user":
kwargs["queryset"] = User.objects.filter(is_superuser=True)
return super(ProfileRecommendationAdmin,self).formfield_for_foreignkey(db_field, request, **kwargs)
or other way is to override the form for that modeladmin class
from django import forms
from django.contrib import admin
from myapp.models import Person
class PersonForm(forms.ModelForm):
class Meta:
model = Person
exclude = ['name']
class PersonAdmin(admin.ModelAdmin):
exclude = ['age']
form = PersonForm
you can change the widget and use something like the selectize with autocomplete and ajax.
models.py=>
from django.db import models
from django.forms import ModelForm
from datetime import date
import datetime
from django import forms
from django.forms import Textarea
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
created = models.DateField(auto_now_add=True)
modified = models.DateField(auto_now_add=True)
def __unicode__(self):
return self.title
class PostModelForm(ModelForm):
class Meta:
model = Post
But I get a text input not textarea for models.TextField(). Is that a reason of css?
I think this section in the documentation should be useful to solve the problem.
from django.forms import ModelForm, Textarea
class PostModelForm(ModelForm):
class Meta:
model = Post
widgets = {
'content': Textarea(attrs={'cols': 80, 'rows': 20}),
}
Alternative to jcollardo's solution (same result, different syntax):
from django import forms
class PostModelForm(forms.ModelForm):
content = forms.CharField(widget=forms.Textarea)
class Meta:
model = Post
You are using models not forms, which means you can't use textarea properly. Instead you can try TextField:
field_name = models.TextField( **options)