Django – CreateView form_valid() how can I access foreign key objects

Question:

I think I complicate this more because what I am using for the Foreign Key has a Choice setting and I have the Foreign Key set to show the human readable choice. Which seems to be what it is searching on. Strikes me as strange though because when I look at the return data it appears to be passing the primary key.

Anyways maybe some code will help so: (as a caveat this is the 9 millionth view iteration and at this point I was at least kind of experimenting with different thing)

views.py

    model = Ship
    fields = ['hull', 'name', 'debt']
    template_name = 'crew/ship.html'
    success_url = reverse_lazy('crew_app:ship_list')

    def form_valid(self, form):
        hul = form.instance.hull
        hp = get_object_or_404(ShipHull, hull=hul)
        form.instance.hull_points_max = hp.hull_points
        form.instance.hull_points_cur = hp.hull_points
        return super().form_valid(form)

models.py

class ShipHull(models.Model):
    SHIP_TYPE = (
        ('WF', 'Worn Freighter'),
        ('RTT', 'Retired Troop Transport'),
        ('SAV', 'Strange Alien Vessel'),
        ('UPS', 'Upgraded Shuttle'),
        ('RSS', 'Retired Scout Ship'),
        ('RSV', 'Repurposed Science Vessel'),
        ('BMV', 'Battered Mining Ship'),
        ('UMC', 'Unreliable Merchant Cruiser'),
        ('FDV', 'Former Diplomatic Vessel'),
        ('ALC', 'Ancient Low-Tech Craft'),
        ('BSW', 'Built from Salvaged Wrecks'),
        ('RMS', 'Retired Military Patrol Ship'),
    )
    hull = models.CharField(max_length=200, choices=SHIP_TYPE)
    traits = ArrayField(models.CharField(max_length=200), blank=True)
    hull_points = models.IntegerField()
    debt = models.CharField(max_length=200)
    roll = IntegerRangeField(null=True)

    def __str__(self):
        return self.get_hull_display()


class Ship(models.Model):

    owner = models.ForeignKey(User, on_delete=models.CASCADE, null=True, blank=True)
    name = models.CharField(max_length=300,)
    debt = models.CharField(max_length=4,)
    hull = models.ForeignKey(ShipHull, on_delete=models.CASCADE, null=True)
    hull_points_max = models.IntegerField(null=True)
    hull_points_cur = models.IntegerField(null=True)
    upgrades = ArrayField(models.CharField(max_length=200), blank=True)

    def __str__(self):
        return self.name 

Here is what I see being returned in the POST:

hull: 1
name: Serenity
debt: 23

If I switch over to trying to use id I get a type error that it can’t be found. Here is the traceback:

Traceback (most recent call last):
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangocorehandlersexception.py", line 47, in inner
    response = get_response(request)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangocorehandlersbase.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangoviewsgenericbase.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangoviewsgenericbase.py", line 98, in dispatch
    return handler(request, *args, **kwargs)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangoviewsgenericedit.py", line 172, in post
    return super().post(request, *args, **kwargs)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangoviewsgenericedit.py", line 142, in post
    return self.form_valid(form)
  File "F:Pythonprofnobodycrew_appviews.py", line 54, in form_valid
    hp = get_object_or_404(ShipHull, id=hul)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangoshortcuts.py", line 76, in get_object_or_404
    return queryset.get(*args, **kwargs)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangodbmodelsquery.py", line 424, in get
    clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangodbmodelsquery.py", line 941, in filter
    return self._filter_or_exclude(False, args, kwargs)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangodbmodelsquery.py", line 961, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, args, kwargs)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangodbmodelsquery.py", line 968, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangodbmodelssqlquery.py", line 1416, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangodbmodelssqlquery.py", line 1435, in _add_q
    child_clause, needed_inner = self.build_filter(
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangodbmodelssqlquery.py", line 1370, in build_filter
    condition = self.build_lookup(lookups, col, value)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangodbmodelssqlquery.py", line 1216, in build_lookup
    lookup = lookup_class(lhs, rhs)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangodbmodelslookups.py", line 25, in __init__
    self.rhs = self.get_prep_lookup()
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangodbmodelslookups.py", line 77, in get_prep_lookup
    return self.lhs.output_field.get_prep_value(self.rhs)
  File "F:Pythonprofnobodyvenvlibsite-packagesdjangodbmodelsfields__init__.py", line 1825, in get_prep_value
    raise e.__class__(
TypeError: Field 'id' expected a number but got <ShipHull: Worn Freighter>.

So what I am trying to figure out, is how to be able to search this value properly.

Because I shared the traceback when I switch over to id here is the view with id:
views.py (again but changed to id)

class CreateShipView(CreateView):
    model = Ship
    fields = ['hull', 'name', 'debt']
    template_name = 'crew/ship.html'
    success_url = reverse_lazy('crew_app:ship_list')

    def form_valid(self, form):
        hul = form.instance.hull
        hp = get_object_or_404(ShipHull, id=hul)
        form.instance.hull_points_max = hp.hull_points
        form.instance.hull_points_cur = hp.hull_points
        return super().form_valid(form)
Asked By: Prof_nobody

||

Answers:

You’re passing an object instance to an int field. Did you try:

hp = get_object_or_404(ShipHull, id=hul.id)

Or even better:

hp = get_object_or_404(ShipHull, pk=hul.pk)
Answered By: xourscode
Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.