How to correctly localize fields in forms? - django

I use Django 3.0.4 and Crispy Forms 1.9.0
I have the following model:
class App(models.Model):
name = models.CharField(max_length=256, db_index=True, verbose_name=_('Name'))
platform = models.ForeignKey(Platform, on_delete=models.CASCADE, verbose_name=_('Platform'))
package_name = models.CharField(max_length=512, unique=True, verbose_name=_('Package name'))
created_at = models.DateTimeField(auto_now_add=True, db_index=True, verbose_name=_('Created Date'))
Form:
class CreateAppForm(forms.ModelForm):
class Meta:
model = App
fields = ('name', 'platform', 'package_name',)
localized_fields = '__all__' # I've tried to enumerate fields as tuple
# labels = { # I've tried to uncomment it
# 'name': _('Name'),
# 'platform': _('Platform'),
# 'package_name': _('Package name'),
# }
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['platform'].queryset = Platform.objects.filter(is_enabled=True)
And template:
{% extends 'base.html' %}
{% load i18n %}
{% load crispy_forms_tags %}
{% block title %}{% trans "Create application" %}{% endblock %}
{% block page_title %}{% trans "Create application" %}{% endblock %}
{% block content %}
<div class="row">
<div class="col">
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">{% trans "Create application" %}</h6>
</div>
<div class="card-body">
<form class="form" action="{% url 'apps:create' %}" method="post">
{% csrf_token %}
{{ form|crispy }}
<button class="btn btn-success btn-lg" type="submit"><i class="fa fa-check"></i> {% trans "Create" %}</button>
</form>
</div>
</div>
</div>
</div>
{% endblock %}
All strings in .po files are localized and compiled (and work everywhere but in forms).
The form renders correctly, but the fields' labels are not localized:
What's wrong?

Django documentation recommends to use gettext_lazy for the verbose names of model fields.
This is essential when calls to these functions are located in code paths that are executed at module load time.
This is something that can easily happen when defining models, forms and model forms, because Django implements these such that their fields are actually class-level attributes. For that reason, make sure to use lazy translations in the following cases:
Model fields and relationships verbose_name and help_text option values

Related

How I can show selectbox from models in my form?

How I can show selectbox from models in my form. Output for below code does not show options for selectbox.
#models.py
class Reg(models.Model):
options = (
('one', 'option1'),
('two', 'option2'),
)
types = models.CharField(max_length=30, choices=options,null=True)
company = models.CharField(max_length=50,null=True)
#form.py
from django import forms
from .models import Reg
class Regform(forms.ModelForm):
class Meta:
model = Reg
fields = ['types','company']
types = forms.CharField(
widget=forms.Select(
attrs={'class': 'form-control'}
),
label="Type",
)
templatefie.html
{% load static %}
{% block main %}
<form method="post" class="post-form" autocomplete="off">
{% csrf_token %}
<div class="row col-md-4 m-auto">
<div>
{{people.as_p}}
<button type="submit" class="save btn btn-primary mt-5">apply</button>
</div>
</div>
</form>
{% endblock %}
Labels and widgets can be defined separately.
Class RegForm(forms.ModelForm):
class Meta:
model = Reg
fields = ['types','company']
labels = {
'types': 'Types',
'company': 'Company'
}
widgets = {
'types': Select(attrs={'class':'form-control'})
}
Create a view function that creates instance of the "Reg" modelform and pass in context to the template context = {'form_obj':Regform()}
{% load static %}
{% block main %}
<form method="post" class="post-form" autocomplete="off">
{% csrf_token %}
<div class="row col-md-4 m-auto">
<div>
{{form_obj.types.label_tag}}
{{form_obj.types}}
<button type="submit" class="save btn btn-primary">apply</button>
</div>
</div>
</form>
{% endblock %}

Edit item within views in Django

There is some problem, I'm trying to update the product on the client by making changes and clicking on the update button - my page is refreshing w/o updating info, so the product has the same data as before. But in the logs, the status code of the GET request is 200 and shows the updated object in the database. When I try to update through the admin Django dashboard, everything works successfully, the problem is only on the client side of the web application. What issues can there be?
Thank you in advance!
models.py
from django.db import models
from django.contrib.auth.models import User
# Create your models here.
CATEGORY = (
('Stationary', 'Stationary'),
('Electronics', 'Electronics'),
('Food', 'Food'),
)
class Product(models.Model):
name = models.CharField(max_length=100, null=True)
quantity = models.PositiveIntegerField(null=True)
category = models.CharField(max_length=50, choices=CATEGORY, null=True)
def __str__(self):
return f'{self.name}'
class Order(models.Model):
name = models.ForeignKey(Product, on_delete=models.CASCADE, null=True)
customer = models.ForeignKey(User, on_delete=models.CASCADE, null=True)
order_quantity = models.PositiveIntegerField(null=True)
def __str__(self):
return f'{self.customer}-{self.name}'
views.py
#login_required(login_url='user-login')
#allowed_users(allowed_roles=['Admin'])
def product_edit(request, pk):
item = Product.objects.get(id=pk)
if request.method == 'POST':
form = ProductForm(request.POST, instance=item)
if form.is_valid():
form.save()
return redirect('dashboard-products')
else:
form = ProductForm(instance=item)
context = {
'form': form,
}
return render(request, 'dashboard/products_edit.html', context)
forms.py
class ProductForm(forms.ModelForm):
class Meta:
model = Product
fields = '__all__'
html template:
{% extends 'partials/base.html' %}
{% block title %}Products Edit Page{% endblock %}
{% load crispy_forms_tags %}
{% block content %}
<div class="row my-4">
<div class="col-md-6 offset-md-3 p-3 bg-white">
<h3>Edit Item</h3>
<hr>
<form>
{% csrf_token %}
{{ form|crispy }}
<input class="btn btn-info" type="submit" value="Confirm">
</form>
</div>
</div>
{% endblock %}
You have forgotten to pass POST method you are using GET.
{% extends 'partials/base.html' %}
{% block title %}Products Edit Page{% endblock %}
{% load crispy_forms_tags %}
{% block content %}
<div class="row my-4">
<div class="col-md-6 offset-md-3 p-3 bg-white">
<h3>Edit Item</h3>
<hr>
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<input class="btn btn-info" type="submit" value="Confirm">
</form>
</div>
</div>
{% endblock %}

Django - UpdateView changes are not saved, success_url is not used

I think I have a pretty basic UpdateView but the object is not saved when I submit the form. The success_url is never called. When I click the Update button, the form refreshes and I stay on the same page. I am able to update the object via admin, so I believe the model is working fine. I am not getting any errors.
urls
path('classroomdetail/<uuid:classroom_id>/',
views.classroomdetail, name='classroomdetail'),
path('classedit/<uuid:pk>/', views.ClassroomUpdateView.as_view(), name='classupdate'),
Model
class Classroom(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
user = models.ForeignKey(settings.AUTH_USER_MODEL,
on_delete=models.CASCADE)
classroom_name = models.CharField(max_length=20)
course = models.ForeignKey(Course, on_delete=models.CASCADE)
students = models.ManyToManyField(Student)
class Meta:
constraints = [models.UniqueConstraint(
fields=['user', 'classroom_name'], name="unique-user-classroom")]
def __str__(self):
return self.classroom_name
views
class ClassroomUpdateView(UpdateView):
model = Classroom
fields = ['classroom_name']
template_name_suffix = '_update'
success_url = reverse_lazy('gradebook:classroom')
template
{% extends 'base.html' %} {% load static %} {% block content %}
{% load crispy_forms_tags %}
<div class="container">
<div class="row">
<div class="col">
<h3>This Classroom belongs to {{ classroom.course }}</h3>
</div>
</div>
<div class="row">
<div class="col-md-3">
<form class="form-group">
{% csrf_token %}{{ form|crispy }}
<input type="submit" class="btn btn-primary mt-2 mb-2" value="Update">
</form>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="ps-2">Cancel
</div>
</div>
</div>
{% endblock content %}
I was missing method="post" in my template. Correct line:
<form method="post" class="form-group">

why i am not getting a followed_by(followers) entry showing up on my page

i am making a twitter like clone(just to learn how things works in django)
so i am basically trying to set up a many_to_many relationship.
i want to add the functionality of showing 'FOLLOWED_BY' and 'FOLLOWING' to a user profile but list of 'FOLLOWED_BY' is not showing on the page please someone help me!
in the models.py i have define two relationship
user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL, related_name='profile', null=True,
blank=True)
following = models.ManyToManyField(settings.AUTH_USER_MODEL,
related_name='followed_by', blank=True)
and in the user_detail.html i have the code for how a profile should look like
this is the models.py module:
from django.conf import settings
from django.db import models
# Create your models here.
class UserProfile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL, related_name='profile',
null=True,
blank=True)
following = models.ManyToManyField(settings.AUTH_USER_MODEL,
related_name='followed_by', blank=True)
def __str__(self):
return str(self.following.all().count())
below is the code for user_detail.html file:
{% extends "base.html" %}
{% block content %}
<div class="row">
<div class="col-sm-3 col-xs-12" style="background-color: yellow">
<h1>{{ object.username }}</h1>
<p>Followers: {{ object.followed_by.count }}</p>
</div>
<div class="col-sm-9 col-xs-12">
<h1>Tweets</h1>
{% for tweet in object.tweet_set.all %}
{{ tweet }}<br/>
{% endfor %}
<hr/>
<h1>Following</h1>
{% for user in object.profile.following.all %}
<a href='/{{ user.username }}'>{{ user.username }}</a><br/>
{% empty %}
<h4>Not following any users</h4>
{% endfor %}
<hr/>
<h1>Followed By</h1>
{% for profile in object.profile.followed_by.all %}
<a href='/{{ profile.user.username }}'>{{ profile.user.username }}</a><br/>
{% empty %}
<h4>Not followed by any user</h4>
{% endfor %}
</div>
{% endblock content %}
for user profile i am getting the FOLLOWING field as i want but FOLLOWED_BY field is not showing how can i do that (what changes should i do in my code)??
You defined a following field that points to the user model, not to a Profile. As a result a Profile has no followed_by relation, a User object has.
I think it probably is better to let following point to Profile, like:
class UserProfile(models.Model):
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
on_delete=models.SET_NULL,
related_name='profile',
null=True,
blank=True
)
following = models.ManyToManyField(
'self',
related_name='followed_by',
symmetrical=False,
blank=True
)
def __str__(self):
return str(self.following.all().count())
Then you can render this like:
<div class="col-sm-3 col-xs-12" style="background-color: yellow">
<h1>{{ object.username }}</h1>
<p>Followers: {{ object.followed_by.count }}</p>
</div>
<div class="col-sm-9 col-xs-12">
<h1>Tweets</h1>
{% for tweet in object.tweet_set.all %}
{{ tweet }}<br/>
{% endfor %}
<hr/>
<h1>Following</h1>
{% for profile in object.profile.following.all %}
<a href='/{{ profile.user.username }}'>{{ profile.user.username }}</a><br/>
{% empty %}
<h4>Not following any users</h4>
{% endfor %}
<hr/>
<h1>Followed By</h1>
{% for profile in object.profile.followed_by.all %}
<a href='/{{ profile.user.username }}'>{{ profile.user.username }}</a><br/>
{% empty %}
<h4>Not followed by any user</h4>
{% endfor %}
</div>
Your code has however some (serious) anti-patterns. The most important one is that you should not write business logic in the template. You should use the view for that. For example you can specify in the view a context like:
context = {
'tweets': object.tweet_set.all()
'followers': object.profile.following.select_related('user').all()
'followed_by': object.profile.followed_by.select_related('user').all()
}
We here can also use a .select_related() [Django-doc] that will boost performance significantly, since now all the users are fetched in the same query.
You also better use the {% url ... %} template tag [Django-doc] to construct queries. So instead of writing:
<a href="/{{ profile.user.username }}">
it is better to construct the query using a reverse lookup like:
<a href="/{% url 'profile_view' username=profile.user.username %}">

Reverse for 'profile_update' with arguments '(1,)' not found. Issue

I have a base url link to access a profile edtting screen but it seems to break my application.
Ive tried so many things that i am getting confused by the different views. I was able to get the form appearing previously but have somehow broken it.
from my base.html(if i remove this line the app start to work again). Im not sure about this user.id parameter im passing in this - is it needed?
<li class="nav-item">
<a class="nav-link" href="{% url 'accounts:profile_update' user.id %}">Edit Profile</a>
</li>
my urls file:
path('profile/edit/', views.ProfileCreate.as_view(), name='profile_update'),
my model:
class Profile(models.Model):
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name='userprofile')
houseNumber = models.CharField(max_length=150, default='')
street = models.CharField(max_length=150, default='')
suberb = models.CharField(max_length=150, default='')
city = models.CharField(max_length=150, default='')
phone = models.CharField(max_length=150, default='')
def __unicode__(self):
return self.user.get_full_name()
def __str__(self):
return self.user
def get_absolute_url(self):
return reverse('account:profile', kwargs=[self.pk])
my form:
class ProfileForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(ProfileForm, self).__init__(*args, **kwargs)
class Meta:
model = Profile
fields = ['user', 'houseNumber', 'street', 'suberb', 'city', 'phone']
the html for the form:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
<div class="container">
<form enctype="multipart/form-data" action="{% url 'accounts:profile_update' %}" method="post" novalidate>
{% csrf_token %}
{{ form|crispy }}
<input name="Submit" type="submit" class="btn btn-success" value="Save"></input>
</form>
<p></p>
</div>
{% endblock %}
Apologies if this question is similar to a few others Ive asked but i just had to restart with this form using a different approach because it just wasnt working properly previously.
According to you url, you don't need to pass user.id through update_profile. So please change the code to:
<a class="nav-link" href="{% url 'accounts:profile_update' %}">Edit Profile</a>