OneToOneField with ForeignKey - django

I have the following situation:
class Professor(models.Model):
default_address = models.OneToOneField('Address')
class Address(models.Model):
name = models.CharField()
street_address = models.CharField()
# ...
professor = models.ForeignKey("Professor", related_name="addresses")
Basically, one Professor can have many Addresses associated with it, but only one default_address selected. However, things aren't working right when I save things with my view, things don't get associated just right:
form = forms.ProfileEditForm(request.POST)
if form.is_valid():
address = professor.default_address or models.Address()
address.name = "Default" if address.name == None else address.name
address.street_address = form.cleaned_data['street_address']
address.street_address_2 = form.cleaned_data['street_address_2']
# ...
address.save()
professor.default_addresss = address
professor.save()
After I save it, professor.default_address is None. What am I doing wrong here?

You have a typo in your code:
professor.default_addresss = address
Should be
professor.default_address = address
Basically, nothing happens on save() for non model attributes.

It looks like you're making the address "default" if no name is supplied. If that's the case, why not just get rid of the One-To-One field and simply create an instance method on the Professor model that gets you the address with the name of "default."
Or, you could create a boolean field on the Address model called is_default (or something along those lines) and make that True if no name is supplied.

I needed to set user_profile on both the Address and the PhoneNumber before saving:
address.user_profile = user.profile
address.save()
phone_number.user_profile = user.profile
phone_number.save()
user.profile.default_address = address
user.profile.default_phone_number = phone_number
user.profile.save()

Related

Model Admin Error: Cannot Exclude Field because it is a Foreign Key to the parent model

My goal is to be able to select a location and Input part numbers without seeing this quote field. I dont even completely understand what this select box is looking for. I have Quote objects saved and yet these are not coming up as selectable options. Not that I want them to, Im just saying. My thinking regarding the seelctable options is that this would be auto-populated? You can probably tell my confusion even in my explanation. Ultimately, I dont want to see a select box at all as Im not really interested in whatever this pointing to, but just for kicks would like to know what it is trying to point to.
quote/Models.py
class Quote(models.Model):
QUOTE_ENVIRONMENTS = (
('testing', 'Test'),
('production', 'Production')
)
SALES_SOURCE=((1, 'Marketplace'),
(2, 'Webstore'),
(3, 'Physical Store'),
(4, 'Phone')
)
environment = models.CharField(max_length=20, choices=QUOTE_ENVIRONMENTS, default="testing")
sales_source = models.IntegerField(choices=SALES_SOURCE, null=True)
order_notes = models.TextField(blank=True)
locations = models.ManyToManyField('products.ProductSelection')
products/models.py
class Product(models.Model):
pass
class Warehouse(models.Model):
pass
class ProductSelection(models.Model):
location = models.ForeignKey('Warehouse', on_delete = models.CASCADE)
product = models.ManyToManyField('Product')
Admin.py
class ProductOrderForm(forms.ModelForm):
locations = forms.ModelChoiceField(queryset= Warehouse.objects.all())
part_number = forms.IntegerField()
def clean_product_id(self):
cd = self.cleaned_data
logger.info(cd)
value = cd['part_number']
if value not in Products.objects.list_part_numbers():
raise forms.ValidationError("Not a valid partnumber")
class ProductSelectionTabularInline(admin.TabularInline):
form = ProductOrderForm
model = Quote.locations.through
class QuoteAdmin(admin.ModelAdmin):
list_display=['id', 'environment', 'order_notes','sales_source']
list_editable = ['environment', 'sales_source', 'order_notes']
inlines = [ProductSelectionTabularInline]
exclude=['quote']
Error when using exclude attr.
ERRORS:
<class 'orders.admin.ProductSelectionTabularInline'>: (admin.E201) Cannot exclude the field 'quote', because it is the foreign key to the parent model 'orders.Quote'.
I dont want the left most box. Thanks for your help
I figure out that the field to the left is the ProductSelection instance. I confused myself by adding the other 2 form widgets. So this does not allow me to do what I want which is to edit the parts to the locations and add it to the form for creating a quote.

Get verbose name of foreign key from objects _meta dynamically in Django

For simplicity sake, with models like the following:
class Plan(models.Model)
name = models.CharField(max_length=255, verbose_name="Phone Plan Name")
monthly_charge = models.FloatField()
class Company(models.Model):
name = models.CharField(max_length=255, verbose_name="Company Full Name")
phone_plan = models.ForeignKey(Plan)
class Client(models.Model):
name = models.CharField(max_length=255, verbose_name="Client Full Name")
company = models.ForeignKey(Company)
Given a string, I want to know if there is an easy way to retrieve the verbose name of a model attribute even if that string traverses through foreign keys.
I know that I can get the verbose name of a Client attribute by
Client._meta.get_field("name").verbose_name
and this would result in "Client Full Name".
But what if I had the string "company__phone_plan__name", I cannot simply use
Client._meta.get_field("company__phone_plan__name").verbose_name
to arrive at "Phone Plan Name" as it yields an error.
These strings will be dynamic, so I am wondering what is the easiest way to arrive at the proper verbose name of an attribute, even if it traverses models?
This particular case is using Django 1.11
This is not so good answer but if you need what you want you can use this function:
def get_verbose_name(model, string):
fields = string.split('__')
for field_name in fields[:-1]:
field = model._meta.get_field(field_name)
if field.many_to_one:
model = field.foreign_related_fields[0].model
elif field.many_to_many or field.one_to_one or field.one_to_many:
model = field.related_model
else:
raise ValueError('incorrect string')
return model._meta.get_field(fields[-1]).verbose_name
This function gets model name and string and return verbose name
You can use it like so:
get_verbose_name(Client, 'company__phone_plan__name')
If you are working with instances of the models, then you could try this:
c = Client.objects.first()
# c._meta.get_field('company').verbose_name
# c.company._meta.get_field('phone_plan').verbose_name
c.company.phone_plan._meta.get_field('name').verbose_name
If you are working with classes only, then its a but more complex:
field_company = Client._meta.get_field('company')
field_phone_plan = field_company.rel.to._meta.get_field('phone_plan')
field_name = field_phone_plan.rel.to._meta.get_field('name')
field_name.verbose_name
# or one long line
s = Client._meta.get_field('company') \
.rel.to._meta.get_field('phone_plan') \
.rel.to._meta.get_field('name') \
.verbose_name
EDIT:
User #AndreyBerenda points out that the second option does not work for him in Django 2.1; I tested only in 1.11, which was specified in the question.

Changes to model with one to one relation not saving

I have two models that I'm relating using Django's OneToOneField, following this documentation: https://docs.djangoproject.com/en/2.0/topics/db/examples/one_to_one/
class Seats(models.Model):
north = models.OneToOneField('User',on_delete=models.CASCADE,related_name='north', default=None, null=True)
bridgetable = models.OneToOneField('BridgeTable',on_delete=models.CASCADE, default=None, null=True)
class BridgeTableManager(models.Manager):
def create_deal(self):
deal = construct_deal()
table = self.create(deal=deal)
s = Seats(bridgetable=table)
s.save()
return table
class BridgeTable(models.Model):
deal = DealField(default=None,null=True)
When I run this code I can successfully get the relationship working
table = BridgeTable.objects.get(pk='1')
user = User.objects.get(username=username)
table.seats.north = user
table.seats.north.save()
print(table.seats.north)
The print statement prints out the name of the player sitting north. But if I try to access the table again like this:
table = BridgeTable.objects.get(pk='1')
print(table.seats.north)
I get "None" instead of the user's name. Is there something I'm missing, like a save that I missed or some concept I'm not understanding? Thanks.
You should save Seats model object that is table.seats.save()
Try print table.seats.north
While table.seats.north.save() runs save on User object
Here are correct steps:
table = BridgeTable.objects.get(pk='1')
user = User.objects.get(username=username)
table.seats.north = user
table.seats.save()
print(table.seats.north)

Django-nonrel on Appengine

class BillList(models.Model):
username = models.ForeignKey(User)
billno = models.CharField(max_length=15)
class OrderDetails(models.Model):
billno = models.ForeignKey(BillList)
orderdetails = models.TextField()
User is the one within django.contrib.auth.models.
I need to retreive all billno of a particular user. How do I go about doing this simple query in Django-nonrel on Appengine?
If I do this:
iq = User.objects.filter(username = "name1")
BillList.objects.filter(username = iq)
Then I get an error: DatabaseError: Subqueries are not supported.
If I try this straight away BillList.objects.filter(username = "restaurant1"), then ValueError: invalid literal for long() with base 10: 'restaurant1'
I'm sure it must be possible to go about doing this simple query! Any workarounds?
The others are correct. But, there may be a fundamental problem here with your understanding of the ForeignKey. For example:
username = models.ForeignKey(User)
That's not really a "username" at all. It is a user object. More understandable would be something like:
user = models.ForeignKey(User)
The User object is what has the username property. So to get a person's username, you would use"
BillList.objects.get(billno = 12345).user.username
Then, your queries become:
iq = User.objects.get(username = "name1")
my_list = BillList.objects.all().filter(user = iq)
Or, more directly:
my_list = iq.billist_set.all()

Is it possible to add a variable to Django Model Field.choices?

Is it possible to add a variable to Django Model Field.choices?
I lose functionality when I have to add it statically.
IP_CHOICES = (
('192.168.1.0', '192.168.1.0'),
)
ip_address = models.IPAddressField(choices=IP_CHOICES, unique=True, blank=True)
I use a Python IP Interpreter called IPy, to calculate the correct IP block.
ip = IP(self.network + slash)
for rangeip in enumerate(ip[2:-1]):
IP_CHOICES = (
("%s" %rangeip, "%s" %rangeip)
)
Is this possible? If so, please help. Been trying to hack it for the past week and got no where. Any help is appreciated.
Please view Model Class.
#IP Block Class
class IP_block(models.Model):
network = models.IPAddressField(unique=True)
slash = models.ForeignKey(Subnet, verbose_name='CIDR')
subnet = models.CharField(max_length=64, blank=True)
gateway_ip = models.CharField(max_length=64, blank=True)
broadcast_ip = models.CharField(max_length=64, blank=True)
ip_range = models.TextField(blank=True, verbose_name='Available IP Range')
dslam = models.ManyToManyField(Dslam, verbose_name='Dslam', blank=True)
#ip block and range save function
def save(self, *args, **kwargs):
slash = unicode(self.slash)
broadcast = IP(self.network + slash).broadcast()
subnet = IP(self.network+slash).strNetmask()
self.broadcast_ip = broadcast
self.subnet = subnet
ip = IP(self.network + slash)
for gateway in ip[1]:
self.gateway_ip = gateway
#rangeip for loop
ip = IP(self.network + slash)
if self.ip_range:
print 'no override'
else:
for rangeip in ip[2:-1]:
self.ip_range += "%s\n" %rangeip
IP_CHOICE = "(%s" %rangeip + ", %s)," %rangeip
#ip_list select
ip_list = models.CharField(choices=IP_CHOICE, max_length=128, blank=True)
super(IP_block, self).save(*args, **kwargs)
class Meta:
verbose_name_plural = 'IP Blocks'
def __unicode__(self):
return self.network
You have many fairly basic errors here. For example, in your suggested syntax:
for rangeip in enumerate(whatever):
IP_CHOICES = (do_something)
It should be obvious to you that you are simply overwriting IP_CHOICES each time through the loop. At the end of the loop, it will simply have the value of the last iteration, which isn't by itself in a suitable format for choices.
You have this same pattern a number of times. Please think about what it is actually doing.
But there's an even worse error in your save function, where you have this line:
ip_list = models.CharField(choices=IP_CHOICE, max_length=128, blank=True)
I have absolutely no idea what you think that is doing. You can't define a field in the middle of a save method. You can set a field's value, but you can't suddenly define a new field (again, please think about it: how would that work with the database? And remember fields are class-level attributes: all instances of that model need to have the same field selection).
It's almost impossible to understand what you are actually trying to do. I think you are trying to provide a choice of IP addresses for one field in the model (ip_list), once the user has set the range in another field (ip_range). (It would have been useful if you'd stated that explicitly up front.)
The place to do this is in the form, not in the model. Setting choices on a model field is really just a shortcut to setting them on forms automatically created from that model, but if you need to do something dynamic you need to define the form yourself and put the logic there. I guess it would be something like this:
class IPBlockForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(IPForm, self).__init__(*args, **kwargs)
if self.instance and self.instance.ip_range:
ip_list_choices = get_ip_list_from_wherever(self.instance_ip_range)
self.fields['ip_list'] = forms.ChoiceField(choices=ip_list_choices)
class Meta:
model = IP_block
But naturally you need to fix the other logic errors in your save method, which I mention above, first.