more than 1 foreign key - django

I have the following models: http://slexy.org/view/s20T8yOiKZ
from mxutils.cms_services import generate_secid
from django.db import models
from django.contrib import admin
from django import forms
class World(models.Model):
title = models.CharField(max_length=150)
secid = models.SlugField(max_length=1000, editable=False)
elements = models.ManyToManyField("Element", related_name='elements', blank=True, null=True)
metadata = models.OneToOneField("Category_metadata", blank=True, null=True)
def save(self):
if not self.pk:
super(World, self).save()
self.secid = generate_secid(self.title, self.pk, World.objects.all())
return super(World, self).save()
def __unicode__(self):
return "%s" % self.title
class Element(models.Model):
parent = models.ForeignKey(World, related_name='element_parent')
world = models.ForeignKey(World, related_name='world', blank=True, null=True)
item = models.ForeignKey("Item", blank=True, null=True)
value = models.DecimalField(default=0, max_digits=5, decimal_places=3)
def save(self):
if self.world and self.item:
return None
elif not self.world and not self.item:
return None
else:
return super(Element, self).save()
def __unicode__(self):
if self.world:
return "%s" % self.world.title
else:
return "%s" % self.item.title
class ElementInline(admin.TabularInline):
model = Element
extra=1
class WorldAdmin(admin.ModelAdmin):
inlines = [ElementInline,]
list_display = ('title',)
ordering = ['title']
search_fields = ('title',)
When I try to click add button for worlds in admin page it shows me the following error:
class 'cms_sample.world_models.Element' has more than 1 ForeignKey to class 'cms_sample.world_models.World'.
I think it's something to do with inline.
What can it be?

Django doesn't know which of the two foreign keys (parent and world) is to be inlined using the ElementInline.
class ElementInline(admin.TabularInline):
model = Element
fk_name = 'parent' #or 'world', as applicable.
extra=1

Related

data of foreign keys are not saving in django admin nested inlines

I'm trying to build a list of replicable fields where the order can be interchanged.
To do so I've built three different models Multicap Multitext Multimg which are Inlines of the model Multis which is an Inline of the model Delta.
I'm using django-nested-admin and everything works fine on the admin page, I can add new objects and change their order.
The problem I have is that when I populate the fields, save the model and then check its content, all the data of the text fields are turned into zeros 0.
instead, when I try to save the image I get this error:
AttributeError: 'int' object has no attribute 'field'
models.py
class Multis(models.Model):
name = models.TextField(max_length=50, null=True, blank=True)
delta = models.ForeignKey('Delta', related_name="delta", null=True, on_delete=models.CASCADE)
class Meta:
ordering = ('name',)
def __str__(self):
return str(self.name)
class Multicap(models.Model):
caption = models.TextField(max_length=50, null=True, blank=True)
multic = models.ForeignKey('Multis', related_name="multicap", null=True, on_delete=models.CASCADE)
class Meta:
ordering = ('caption',)
def __str__(self):
return str(self.caption)
class Multimg(models.Model):
img = models.ImageField(upload_to="images", verbose_name='Image', null=True, blank=True,)
multim = models.ForeignKey('Multis', related_name="multimg", null=True, on_delete=models.CASCADE)
class Meta:
ordering = ('img',)
#property
def img_url(self):
if self.img and hasattr(self.img, 'url'):
return self.img.url
def get_image_filename(instance, filename):
title = instance.post.title
slug = slugify(title)
return "post_images/%s-%s" % (slug, filename)
def get_absolute_url(self):
return reverse('delta-detail', kwargs={'pk': self.pk})
class Multitext(models.Model):
text = tinymce_models.HTMLField(null=True, blank=True)
multit = models.ForeignKey('Multis', related_name="multitext", null=True, on_delete=models.CASCADE)
class Meta:
ordering = ('text',)
def __str__(self):
return str(self.text)
class Delta(models.Model):
heading = models.CharField(max_length=50, null="true")
date_posted = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
class Meta:
ordering = ('heading',)
def __str__(self):
return str(self.heading)
def get_absolute_url(self):
return reverse('delta-detail', kwargs={'pk': self.pk})
admin.py
from django.contrib import admin
import nested_admin
from nested_admin import SortableHiddenMixin, NestedTabularInline, NestedModelAdmin, NestedStackedInline
from .models import Delta, Multimg, Multitext, Multicap, Multis
class MimgAdmin(nested_admin.NestedStackedInline):
model = Multimg
sortable_field_name = "img"
extra = 0
class MtxtAdmin(nested_admin.NestedStackedInline):
model = Multitext
sortable_field_name = "text"
extra = 0
class McapAdmin(nested_admin.NestedStackedInline):
model = Multicap
sortable_field_name = "caption"
extra = 0
class MAdmin(nested_admin.SortableHiddenMixin, nested_admin.NestedStackedInline):
model = Multis
sortable_field_name = "name"
extra = 0
inlines = [ McapAdmin, MtxtAdmin, MimgAdmin ]
#admin.register(Delta)
class DeltaAdmin(nested_admin.NestedModelAdmin):
sortable_field_name = "delta"
inlines = [ MAdmin ]
Solved. It was a stupid error related to sortable_field_name
This field needs to be related to a field in the model which has to be formulated this way:
models.py
position = models.PositiveSmallIntegerField("Position", null=True)
admin.py
sortable_field_name = 'position'
This forms a position field that has a basic value of 0. Doing the way I was doing so this value was being substituted for the field I wanted to fill in.
Hope this can help someone occuring in the same error

How to set specific conditions in models.py while entering data in admin panel?

room_category = models.ForeignKey(Cat, on_delete=models.CASCADE)
number = models.IntegerField(unique=True)
people = models.IntegerField()
picture = models.ImageField(upload_to = 'room/', null=True, blank=True)
actual_price = models.IntegerField()
offer_price = models.IntegerField()
def __str__(self):
return '%d : %s with People : %d' % (self.number, self.room_category, self.people)
I want to set a condition in offer_price table that offer_price < actual_price. It should show an error while entering data in the admin panel itself.
You can add validation in the .clean(…) method [Django-doc], and add a database constraint with Django's constraint framework:
from django.core.exceptions import ValidationError
from django.db.models import F, Q
class YourModel(models.Model):
# …
actual_price = models.IntegerField()
offer_price = models.IntegerField()
def clean(self, *args, **kwargs):
if self.actual_price <= self.offer_price:
raise ValidationError('offer price should be lower than the actual price')
return super().clean(*args, **kwargs)
class Meta:
constraints = [
models.CheckConstraints(
check=Q(offer_price__lt=F('actual_price')),
name='lower_than_actual_price'
)
]

showing entries based on selection type in django

my question is i want to show only particular titles under music_track (musicmodel)field when type = track(title model) in my django admin site
class album(models.Model):
def get_autogenerated_code():
last_id = album.objects.values('id').order_by('id').last()
if not last_id:
return "AL-"+str(0)
return "AL-"+str(last_id['id'])
album_name = models.CharField( max_length=150, blank=False )
music_track = models.ManyToManyField("title")
def __str__(self):
return (self.album_name)
class Meta:
verbose_name = "Album"
verbose_name_plural = "Albums"
class title(models.Model):
def get_autogenerated_code():
last_id = title.objects.values('id').order_by('id').last()
if not last_id:
return "TT-"+str(0)
return "TT-"+str(last_id['id'])
upc_code = models.CharField(max_length=15, default="N/A", blank=False)
display_name = models.CharField(max_length=150, blank=False)
type = models.ForeignKey(Asset_Type, on_delete=models.CASCADE, null=True)
def __str__(self):
return (self.display_name+ " " +self.code)
admin.site.register( [album, title] )
From your question, I am understanding that while creating an album in your admin panel, you require that the music_track should only show the titles having type as track. My solution for this is:
In your admin.py file
from .models import title, album, Asset_type
class AlbumForm(forms.ModelForm):
class Meta:
model = Product
fields = ('album_name', 'music_track', )
def __init__(self, user, *args, **kwargs):
super(AlbumForm, self).__init__(*args, **kwargs)
type = Asset_type.objects.get(type='track')
self.fields['music_track'].queryset = Title.objects.filter(type=type)
class MyModelAdmin(admin.ModelAdmin):
form = AlbumForm
admin.site.register(album, MyModelAdmin)
Maybe this can give you the idea you need.

Django Form Validators on Comparing One Object Against Another

I am creating a Non Disclosure Agreement form that a user fills out after registering and logging in. I am using a custom signup form with AllAuth and pre-populating parts of the form. I pre-populate the first and last name into the top part of the form as shown below in first screen shot, but as a part of the digital signature I am setting up; I need to validate the typed signature field matches the name of the first_name and the last_name concatenated together per the second screen-shot. I know I need to setup a validator based on Django Form & Field Validations and I've tried several things but just can't get my mind rapped around it. Any help putting this together would be huge...thank you.
My Models
class Profile(models.Model):
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="profile", verbose_name="user")
...
class NonDisclosure(Timestamp):
profile = models.ForeignKey(Profile, on_delete=models.CASCADE, related_name="nda", verbose_name="profile")
user_signature = models.CharField(max_length=250, verbose_name='Signature')
user_street = models.CharField(max_length=250, verbose_name='Street Address')
user_city = models.CharField(max_length=250, verbose_name='City')
user_state = models.CharField(max_length=2, verbose_name='State Initials')
user_zip = models.IntegerField(verbose_name='Zip Code')
phone = models.CharField(max_length=25, verbose_name='Phone Number')
cash_on_hand = models.CharField(max_length=250, verbose_name='Cash on Hand')
value_of_securities = models.CharField(max_length=250, verbose_name='Value of Securities')
equity_in_real_estate = models.CharField(max_length=250, verbose_name='Equity on Real Estate')
other = models.CharField(max_length=250, verbose_name='Other Assets')
#property
def username(self):
return self.profile.username
#property
def first_name(self):
return self.profile.first_name
#property
def last_name(self):
return self.profile.last_name
#property
def email(self):
return self.profile.email
class Meta:
verbose_name = 'Non Disclosure Agreement'
verbose_name_plural = 'Non Disclosure Agreements'
def __str__(self):
return "%s" % self.profile
def get_absolute_url(self):
return reverse('nda_detail', args=[str(self.id)])
My Views:
class NonDisclosureForm(BaseModelForm):
cash_on_hand = forms.CharField(required=False)
value_of_securities = forms.CharField(required=False)
equity_in_real_estate = forms.CharField(required=False)
other = forms.CharField(required=False)
class Meta:
model = NonDisclosure
fields = ['user_signature', 'user_street', 'user_city', 'user_state', 'user_zip', 'phone', 'cash_on_hand', 'value_of_securities', 'equity_in_real_estate', 'other']
class NdaCreate(CreateView):
form_class = NonDisclosureForm
template_name = 'nda/nda_form.html'
def form_valid(self, form):
form.instance.profile = Profile.objects.get(user=self.request.user)
form.instance.created_by = self.request.user
return super(NdaCreate, self).form_valid(form)
Firstly, you should subclass ModelForm, not BaseModelForm. Write a clean_<fieldname> method for your user_signature field, and make sure that the value is as expected. You can access self.instance.created_by to check.
class NonDisclosureForm(ModelForm):
...
class Meta:
model = NonDisclosure
fields = ['user_signature', ...]
def clean_user_signature(self):
user_signature = self.cleaned_data['user_signature']
expected_name = '%s %s' % (self.instance.created_by.first_name, self.instance.created_by.last_name)
if user_signature != expected_name:
raise forms.ValidationError('Signature does not match')
return user_signature
Then you need to update your view so that it sets instance.created_by. You can do this by overriding get_form_kwargs.
class NdaCreate(CreateView):
def get_form_kwargs(self):
kwargs = super(NdaCreate, self).get_form_kwargs()
kwargs['instance'] = NonDisclosure(created_by=self.request.user)
return kwargs

django-piston: how to get values of a many to many field?

I have a model with ManyToManyField to another model. I would like to get all the info on a particular record (including the related info from other models) return by JSON.
How to get django-piston to display those values? I would be happy with just primary keys.
Or can you suggest another option ?
I may be wrong, but this should do it:
class PersonHandler(BaseHandler):
model = Person
fields = ('id', ('friends', ('id', 'name')), 'name')
def read(self, request):
return Person.objects.filter(...)
You need to define a classmethod on the handler that returns the many-to-many data, I don't believe Piston does this automatically.
class MyHandler(BaseHandler):
model = MyModel
fields = ('myfield', 'mymanytomanyfield')
#classmethod
def mymanytomanyfield(cls, myinstance):
return myinstance.mymanytomanyfield.all()
My code:
Models:
class Tag(models.Model):
"""docstring for Tags"""
tag_name = models.CharField(max_length=20, blank=True)
create_time = models.DateTimeField(auto_now_add=True)
def __unicode__(self):
return self.tag_name
class Author(models.Model):
"""docstring for Author"""
name = models.CharField(max_length=30)
email = models.EmailField(blank=True)
website = models.URLField(blank=True)
def __unicode__(self):
return u'%s' % (self.name)
class Blog(models.Model):
"""docstring for Blogs"""
caption = models.CharField(max_length=50)
author = models.ForeignKey(Author)
tags = models.ManyToManyField(Tag, blank=True)
content = models.TextField()
publish_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now=True)
def __unicode__(self):
return u'%s %s %s' % (self.caption, self.author, self.publish_time)
Handle:
class BlogAndTagsHandler(BaseHandler):
allowed_methods = ('GET',)
model = Blog
fields = ('id' 'caption', 'author',('tags',('id', 'tag_name')), 'content', 'publish_time', 'update_time')
def read(self, request, _id=None):
"""
Returns a single post if `blogpost_id` is given,
otherwise a subset.
"""
base = Blog.objects
if _id:
return base.get(id=_id)
else:
return base.all() # Or base.filter(...)
Works petty good.