Adapt a class method to another class - python

#python_2_unicode_compatible
class EmployerProfile(AbstractAddress):
customer = models.OneToOneField(
CustomerProfile, verbose_name=_('Customer'),
related_name='employerprofile')
company_name = models.CharField(_('Company name'),
max_length=50, blank=True, null=True)
...
#That function will empty all fields in this model whenever income_source
# is different of 'Employed'
def empty_fields(self):
if self.income_source != 'Employed':
to_empty = [f.name for f in EmployerProfile._meta.get_fields()]
exclude = [u'id', "has_missing_fields",
"manual_validation", "actor_actions",
"target_actions", "action_object_actions",
]
for field_name in to_empty:
if field_name not in exclude:
setattr(self, field_name, None)
#python_2_unicode_compatible
class FinancialProfile(models.Model):
customer = models.OneToOneField(
CustomerProfile, verbose_name=_('Customer'),
related_name='financialprofile')
income_source = models.CharField(_('Income source'), max_length=20,
choices=settings.LOANWOLF_INCOME_SOURCE_CHOICES,
default='employed')
I would like to move empty_field() in the class FinancialProfile. In fact, this method must emptied the attributes from EmployerProfile whenever income_source (FinancialProfile) != 'Employed'. Could anyone be able to tell me how could I do such thing?
P.S. Please tell me if there's something unclear.
Thanks!

Related

Resolving a ForeignKey field pointing to the model it belongs to

I have a model class in Django which has a ForeignKey referencing the model it actually belongs to:
class Foo(models.Model):
name = models.CharField(max_length=256, verbose_name="Name")
#... some other fields
bar = models.ForeignKey(
"self", on_delete=models.CASCADE, null=True, blank=True
)
def __str__(self):
return self.name
I want to add a custom method in that class which resolves, on the fly, the name in a new field, e.g. bar_resolved when instantiating it in a QuerySet in a view:
from .models import Foo
foo = Foo.objects.all()
# do stuff
I've tried this:
class Foo(models.Model):
name = models.CharField(max_length=256, verbose_name="Name")
#... some other fields
bar = models.ForeignKey(
"self", on_delete=models.CASCADE, null=True, blank=True
)
# preparing the resolved bar field which should contain the 'name' value corresponding to the id:
bar_resolved = models.CharField(
max_length=256,
verbose_name="Bar name resolved",
null=True
)
def __str__(self):
return self.name
def resolve(self):
if self.bar:
self.bar_resolved = self.bar.name
return super(Foo, self).resolve()
Then in my view:
from .models import Foo
foo = Foo.objects.all()
foo.resolve()
but it raises: 'QuerySet' object has no attribute 'resolve'
How could I achieve that? and do I need to hard code a 'resolved' field in my model for that (I think it's overkill to do so)?
I do not understand why would you have a Foreing key referencing self in the database.
Instead of using resolve, you could probably do it on the save long before - i.e. when setting value of "bar"
Another idea that comes to mind is setting it in the __ init__
method of the model link to Stack
hope this helps.
def save(self, force_insert: bool = False, force_update: bool = False) -> None:
if self.field is None:
self.field = "value"
# and so on...
return super().save(force_insert, force_update)
One way is to annotate..[Django-doc] your queryset using F expressions..[Django-doc] with bar's name field:
from django.db.models import F
foos = Foo.objects.annotate(bar_resolved=F("bar__name")).all()
for foo in foos:
print(foo.bar_resolved)

UpdateView does not show existing data when using filter

I am quite new with Django and I need help.
My problem is quite similar what Mike had in his case:
UpdateView not populating form with existing data, but I have not found solution yet.
My goal is to view owner dropdown selection list only those users who are members of the organization.
models.py
# organizations.models.py
...
from accounts.models import User
from core.models import TimeStampModel
...
class Organization(TimeStampModel, models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
name = models.CharField(
verbose_name=_('Name'),
max_length=255,
unique=True
)
code = models.CharField(
verbose_name=_('Code'),
max_length=255,
null=True,
blank=True
)
owner = models.ForeignKey(
User,
on_delete=models.PROTECT,
verbose_name=_('Owner'),
related_name='owner',
help_text=_('Organization Owner and Contact Person'),
)
slug = models.SlugField(verbose_name=_('Organization key'), unique=True)
...
class Meta:
verbose_name = _('Organization')
verbose_name_plural = _('Organization')
ordering = ['name', 'code']
def __str__(self):
return f'{self.name}, {self.code}'
# Create automatically slug value from organization name field.
# In case similar is exist then add extra count digit end of slug.
def _get_unique_slug(self):
slug = slugify(self.name)
unique_slug = slug
num = 1
while Organization.objects.filter(slug=unique_slug).exists():
unique_slug = '{}-{}'.format(slug, num)
num += 1
return unique_slug
def save(self, *args, **kwargs):
if not self.slug:
self.slug = self._get_unique_slug()
self.next_update = timezone.now() + relativedelta(
months=self.update_interval)
super(Organization, self).save(*args, **kwargs)
def get_absolute_url(self):
kwargs = {
'slug': self.slug
}
return reverse('organization_main_page', kwargs=kwargs)
class OrganizationMembers(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
organization = models.ForeignKey(
Organization,
on_delete=models.CASCADE,
verbose_name=_('Organization')
)
member = models.ForeignKey(
User,
on_delete=models.CASCADE,
verbose_name=_('Member'),
null=True,
blank=True
)
organization_admin = models.BooleanField(
verbose_name=_('Organization admin'),
default=False
)
class Meta:
verbose_name = _('Organization: Member')
verbose_name_plural = _('Organization: Members')
ordering = ['organization', 'member']
unique_together = ('organization', 'member')
def __str__(self):
return f'{self.member}'
def get_absolute_url(self):
kwargs = {
'slug': self.slug
}
return reverse('organization_detail', kwargs=kwargs)
forms.py
# organizations.forms.py
....
from accounts.models import User
from .models import Organization, OrganizationMembers
...
class OrganizationUpdateForm(forms.ModelForm):
class Meta:
model = Organization
fields = '__all__'
exclude = ('date_created', 'created_by', 'created_by_id',
'last_updated', 'last_updated_by', 'last_updated_by_id',
'next_update', 'slug')
# Restrict user selection lists to view only members of the organization
def __init__(self, *args, **kwargs):
inst = kwargs.get('instance', None)
super(OrganizationUpdateForm, self).__init__(*args, **kwargs)
self.fields['owner'].queryset = OrganizationMembers.objects.\ # <--- !!!
filter(organization_id=inst.id)
In the forms.py, if I comment out self.field['owner]... line, then owner field will show saved value from database, but then I can see all users in the dropdown list. When queryset is enabled then selection list show correct users, but saved value is not visible.
views.py
# organizations.views.py
from .forms import OrganizationUpdateForm
from accounts.models import User
from .models import Organization, OrganizationMembers
class OrganizationUpdateView(LoginRequiredMixin, UpdateView):
model = Organization
form_class = OrganizationUpdateForm
template_name = 'organizations/organization_update.html'
success_url = reverse_lazy('organizations')
# Save data and set current user to last updated by fields
def form_valid(self, form):
object = form.save(commit=False)
object.last_updated_by = self.request.user.get_full_name()
object.last_updated_by_id = self.request.user
return super(OrganizationUpdateView, self).form_valid(form)
def get_queryset(self):
criteria1 = Q(owner=self.request.user)
criteria2 = Q(organizationmembers__member=self.request.user)
criteria3 = Q(organizationmembers__organization_admin=1)
org_list = Organization.objects.\
filter(criteria1 | (criteria2 & criteria3)).distinct()
if org_list.count() != 0:
return org_list
else:
raise Http404('You don\'t have permissions!')
In Mikes case Chiheb has commented that "With UpdateView it's a little bit tricky. So, in order to initialize your form's data, you need to do it in the view itself not in the form."
What is the reason that cannot add filter to UpdateView?
Please can someone help me to solve my problem. Thanks.
UPDATE
Not filtered. Value from database is visible
Not filtered. Dropdown list show all users in the system
Filter enabled. Value is not visible
Filter enabled. Dropdown list show correct valeus
The problem is that owner in your models is a FK to User model, but you are filtering queryset in form by OrganizationMembers. Make it the same and the problem should be gone.

Save Instance Nested Serializers in Django Rest Framework

I have problem with saving instance Live_In Nested Serializers in Django Rest Framework. Hope your guys help me! I think just a basic issue.
My Serializers:
I think it comes error when I write saving instance
class CityLiveInSerializer(ModelSerializer):
country = CharField(required=False, source='country.name')
class Meta:
model = City
fields = [
'name',
'slug',
'country'
]
class UserEditSerializer(ModelSerializer):
live_in = CityLiveInSerializer(source='profile.live_in')
about = serializers.CharField(source='profile.about')
class Meta:
model = User
fields = [
'username',
'live_in',
'about',
]
def update(self, instance, validated_data):
instance.username = validated_data.get('username', instance.username)
instance.save()
# Update Serializers Profile
if (validated_data.get('profile') is not None):
profile_data = validated_data.pop('profile')
profile = instance.profile
profile.about = profile_data.get('about', profile.about)
profile.save()
if (validated_data.get('live_in') is not None):
live_in_data = validated_data.pop('live_in')
try:
city = City.objects.get(name=live_in_data['name'])
except City.DoesNotExist:
city = City.objects.create(**live_in_data)
instance.profile.live_in = city
instance.profile.save()
return instance
My City Model (Live_in)
class City(BaseCity):
class Meta(BaseCity.Meta):
swappable = swapper.swappable_setting('cities', 'City')
class BaseCity(Place, SlugModel):
name = models.CharField(max_length=200, db_index=True, verbose_name="standard name")
country = models.ForeignKey(swapper.get_model_name('cities', 'Country'), related_name='cities', null=True, blank=True)
My Profile Model
class Profile(models.Model):
# Extend User
user = models.OneToOneField(User, unique=True)
about = models.TextField(max_length=1000, default='', blank=True, null=True)
live_in = models.ForeignKey(City, null=True, blank=True, related_name="live_in")
Data sent by Postman (Json)
{ "live_in": { "name": "Encamp" } }
TraceError:
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/rest_framework/serializers.py" in data
263. self._data = self.to_representation(self.instance)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/rest_framework/serializers.py" in to_representation
488. attribute = field.get_attribute(instance)
File "/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/site-packages/rest_framework/fields.py" in get_attribute
463. raise type(exc)(msg)
Exception Type: AttributeError at /api/v1/users/account/edit/
Exception Value: Got AttributeError when attempting to get a value for field live_in on serializer UserEditSerializer.
The serializer field might be named incorrectly and not match any attribute or key on the User instance.
Original exception text was: 'User' object has no attribute 'live_in'.
First of all you dont need many=True in this case. It required for related objects list, but you will pass only one city.
Secondly live_in is attribute of profile model so you need to update profile and add source argument:
live_in = CityLiveInSerializer(source="profile.live_in")
def update(self, instance, validated_data):
instance.username = validated_data.get('username', instance.username)
instance.save()
# Update Serializers Profile
if (validated_data.get('profile') is not None):
profile_data = validated_data.pop('profile')
profile = instance.profile
profile.about = profile_data.get('about', profile.about)
if (profile_data.get('live_in') is not None):
live_in_data = profile_data.pop('live_in')
try:
city = City.objects.get(name=live_in_data["name"])
except City.DoesNotExist:
city = City.objects.create(**live_in_data)
profile.live_in = city
profile.save()
return instance
In this case you need to allow create city without country, so you need to add null=True, blank=True to country attribute:
class BaseCity(Place, SlugModel):
name = models.CharField(max_length=200, db_index=True, verbose_name="standard name")
country = models.ForeignKey(swapper.get_model_name('cities', 'Country'),
related_name='cities', null=True, blank=True)

Django: Parent categories doesn't shown in admin interface

Problem:
Dash signs display in 'Parent category' column before I open any menu item. I get 'Parent category' items when open menu item profile(see admin.py below)
Before and after opening of menu profile: picture1 and picture2 (pay attention on Parent Category column)
My models.py: (attention on parent_id)
class Menu(models.Model):
cat_title = models.CharField(max_length=150, verbose_name='Category title')
menu_title = models.CharField(max_length=150, verbose_name='Menu title')
parent_id = models.IntegerField(blank=True, null=True, verbose_name='Parent category', choices=(('',''),))
url = models.CharField(max_length=255, verbose_name='URL', blank=True)
named_url = models.CharField(max_length=255, verbose_name='Named URL', blank=True)
level = models.IntegerField(default=0, editable=False)
My admin.py: ()
class MyMenu(admin.ModelAdmin):
def get_choices(self):
choices = (('',''),)
categories = models.Menu.objects.all().values()
for i in categories:
choices += ((i['id'], i['cat_title']),)
return choices
def formfield_for_choice_field(self, db_field, request):
if db_field.name == 'parent_id':
db_field.choices = self.get_choices()
return super().formfield_for_choice_field(db_field, request)
list_display = ('cat_title', 'menu_title', 'parent_id', 'level')
list_display_links = ('cat_title', 'menu_title')
admin.site.register(models.Menu, MyMenu)
Question: How could I rewrite my admin.py to show parent_id items without opening any menu item profile?
I've already tried Model.get_FOO_display() but it doesn't work in right way. Any help would be appreciated.
Without changing your model, the simplest solution is probably to add a parent method in your admin and use it instead of "parent_id" in the list_display list:
class MyMenu(admin.ModelAdmin):
# ....
def parent(self, obj):
if obj.parent_id:
return Menu.objects.get(pk=obj.parent_id).cat_title
return ""
list_display = ('cat_title', 'menu_title', 'parent', 'level')
# Unrelated but you may also want to rewrite `get_choices`
# in a simpler and more performant way:
def get_choices(self):
choices = models.Menu.objects.values_list("id", "cat_title"))
return (('',''),) + tuple(choices)
Or to make parent a method or property on your Menu model:
class Menu(models.Model):
# ...
# you may want to use django's `cached_property` instead
# but then you'll have to invalidate the cache when setting
# (or unsetting) `.parent_id`
#property
def parent(self):
if not self.parent_id:
return None
return Menu.objects.get(pk=self.parent_id)
and add "parent" to your admin's list_display.
BUT since Menu.parent_id is actually a foreign key on Menu, the proper solution is to declare it as such in your model:
class Menu(models.Model):
cat_title = models.CharField(max_length=150, verbose_name='Category title')
menu_title = models.CharField(max_length=150, verbose_name='Menu title')
parent = models.ForeignKey("self", blank=True, null=True, related_name="children")
# etc

get() returned more than one Children -- it returned 2! in django

I have a linked model:
class Children(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
child_name = models.CharField(max_length=150, null=True, blank=True)
slug = AutoSlugField(populate_from='child_name')
blood_group = models.CharField(max_length=5, blank=True)
class Meta:
unique_together = ('slug', 'person')
def get_absolute_url(self):
return self.person.get_absolute_url()
def get_delete_url(self):
return reverse(
'member:children-delete',
kwargs={
'person_slug': self.person.slug,
'children_slug': self.slug})
def get_update_url(self):
return reverse(
'member:children-update',
kwargs={
'person_slug': self.person.slug,
'children_slug': self.slug})
my forms.py:
class ChildrenForm( SlugCleanMixin, forms.ModelForm):
class Meta:
model = Children
exclude = ('person',)
def clean(self):
cleaned_data = super().clean()
slug = cleaned_data.get('slug')
person_obj = self.data.get('person')
exists = (
Children.objects.filter(
slug__iexact=slug,
person=person_obj,
).exists())
if exists:
raise ValidationError(
"Children with this Slug "
"and Person already exists.")
else:
return cleaned_data
def save(self, **kwargs):
instance = super().save(commit=False)
instance.person = (
self.data.get('person'))
instance.save()
self.save_m2m()
return instance
views.py:
class ChildrenCreate( ChildrenFormMixin, ChildrenGetObjectMixin,
PersonContextMixin,CreateView):
template_name = 'member/children_form.html'
model = Children
form_class = ChildrenForm
class ChildrenUpdate(ChildrenFormMixin, ChildrenGetObjectMixin,
PersonContextMixin,UpdateView):
template_name = 'member/children_form.html'
model = Children
form_class = ChildrenForm
slug_url_kwarg = 'children_slug'
class ChildrenDelete(ChildrenFormMixin,ChildrenGetObjectMixin,
PersonContextMixin,DeleteView):
model = Children
slug_url_kwarg = 'children_slug'
def get_success_url(self):
return (self.object.person
.get_absolute_url())
my utils.py:
class ChildrenFormMixin():
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
if self.request.method in ('POST', 'PUT'):
self.person = get_object_or_404(
Person,
slug__iexact=self.kwargs.get(
self.person_slug_url_kwarg))
data = kwargs['data'].copy()
data.update({'person': self.person})
kwargs['data'] = data
return kwargs
class ChildrenGetObjectMixin():
def get_object(self, queryset=None):
person_slug = self.kwargs.get(
self.person_slug_url_kwarg)
children_slug = self.kwargs.get(
self.slug_url_kwarg)
return get_object_or_404(
Children,
slug__iexact=children_slug,
person__slug__iexact=person_slug)
class PersonContextMixin():
person_slug_url_kwarg = 'person_slug'
person_context_object_name = 'person'
def get_context_data(self, **kwargs):
person_slug = self.kwargs.get(
self.person_slug_url_kwarg)
person = get_object_or_404(
Person, slug__iexact=person_slug)
context = {
self.person_context_object_name:
person,
}
context.update(kwargs)
return super().get_context_data(**context)
The children created more than one for same name of same parents. When I tried to edit children it gives "get() returned more than one Children -- it returned 2!" error. In traceback, it said, 'person__slug__iexact=person_slug' is the direct causes of this traceback.
In the form, I added clean method to catch the error and maintain uniqueness of children name of same parents but it not worked. Could I get suggestions where I do wrong?
Edit:
my Person model:
class Person(models.Model):
name = models.CharField(max_length=250)
slug = AutoSlugField(populate_from='name')
birth_date = models.DateField(null=True, blank=True)
blood_group = models.CharField(max_length=5)
present_address = models.CharField(max_length=250, blank=True)
permanent_address = models.CharField(max_length=250, blank=True)
user = models.OneToOneField(
settings.AUTH_USER_MODEL,
related_name='member_persons')
class Meta:
ordering = ['name']
unique_together = ['name', 'birth_date']
I believe you are using AutoSlugField from django-autoslug, and you trying to get by non-unique field. AutoSlugField won't make your field unique by default, from docs:
AutoSlugField can also perform the following tasks on save:
populate itself from another field (using populate_from),
use custom slugify function (using slugify or Settings), and
preserve uniqueness of the value (using unique or unique_with).
None of the tasks is mandatory, i.e. you can have auto-populated non-unique fields, manually entered unique ones (absolutely unique or within a given date) or both.
So quick fix would be slug = AutoSlugField(populate_from='child_name', unique=True)
UPDATE(Since you posted your Person model)
The problem is the same and solution is the same.
Explanation:
For example you have two Person objects:
id name slug birth_date
1 alex alex 10.10.2016
2 alex alex 10.10.2015
This won't violate unique_together = ['name', 'birth_date']
And you got two Children objects:
id name slug person_id
1 john john 1
2 john john 2
And that won't violate unique_together = ('slug', 'person') neither
Then you are making query
get_object_or_404(
Children,
slug__iexact='john',
person__slug__iexact='alex')
Which would match two objects. So you got problem. Quick fix would be to make slug unique=True.

Categories

Resources