• πŸ”— Travel photos are underrated

    I snapped many photos while wandering Haifa last week, as I always do whenever I see something new or interesting while exploring a city. I was with my friend Tyler, who conspicuously does not take photos while exploring a new place. I asked him why,...
    This is one reason I made Trips. I wanted a way to re-visit my trips. Having your photos and a map make it so much more enjoyable to remember than a pro photo someone else took.
    1. Tagged with
    2. travel
    3. photography
  • Getting some ideas for how I want photo posts to work in Tanzawa. Think I’m close to being able to start on it.
  • The Week #90

    • After a few months delay I went and got my annual health check. I was able to go to the same center as last time, too. I'm always amazed at the efficientcy of these places as each person is directed from station to station and the nurses/technicians do their thing.

      Despite my best efforts I gained a couple of kilos and my blood pressure was slightly up (with the state of the world how could it not πŸ˜…). It's time to cut out that snacking habit I've gotten into as of late and cut down portions at dinner.
    • As Leo had a "forced" Tuesday off last week, since he wasn't sick, rather than be stressed at the house all day we went out and did our usual Monorail-Enoshima-Aquarium-Burger loop. He played the crane game with his own money once, but he didn't win anything, but he seemed satisfied.

      While we were walking to the Enoden after lunch, Leo decided he wanted some sweets from the coffee shop, so we dropped by Tully's as well. There's a great word in Japanese for exactly this situation, where you have room for dessert despite being full: betsu-bara. Betsu means separate/different and bara being stomach. It's the first time he's had dessert stomach that I can remember.
    • There was a big earthquake last week around 11:30pm. It was certainly the biggest and longest quake I've felt in a while. Thankfully there wasn't any damage around here and it was mostly an annoyance because it kicks off your adrenaline, which is not helpful when you're trying to get back to sleep.
    • I bought tickets for Disney next month during Leo's spring break. I've never been to Tokyo Disneyland, but I've been to Disney Sea twice.

      As a kid, we lived close enough that when we went to Disney it was just for the day, never staying at the hotels. And being honest, they were probably also out of budget, even if it wasn't so close. While we're still close enough here in Yokohama for a day-trip to Disney (trains rule), we're going to stay one night. I'm excited about it, not just because it's a surprise for Leo, but because I can do those family things I couldn't do myself growing up.
    • Unrelated to health check results, but after skipping a week of running not on purpose I got back out and ran 5km. I always feel so much better after running, no matter how long I go out. I need to remind myself when the main obstacle is me changing clothes. Sometimes I can't run in the morning or don't want a tight lunch run. With the days starting to get longer, I wonder if I could do short post-work runs every now and again...
  • I've now added support for OpenGraph meta tags in Tanzawa. It's nice to get a small feature out.

    Open graph data on a blog post

    Thanks to Ricardo for requesting this feature.Β 
  • How to Resolve Overlapping DjangoObjectTypes with Graphene and Graphene Relay

    If your schema has multiple types defined using DjangoObjectType for the same model in a Union, selections that work on the Union won't necessarily work as is on Relay node queries.Β 

    For example, suppose we had a model schema like this:

    class Record(models.Model):
        record_type = models.CharField(max_length=12)
        
        @property
        def is_disco(self) -> bool:
            return self.record_type == "DISCO"
    
        @property
        def is_punk(self) -> bool:
            return self.record_type == "PUNK"
    
    class Disco(models.Model):
        record = models.OneToOneField(Record, related_name="disco_record")
        bpm = models.IntegerField()
    
    
    class Punk(models.Model):
        record = models.OneToOneField(Record, related_name="punk_record")
        max_chords = models.IntegerField()

    Our application cares Records and, depending on the record_type, the type of meta information we want to manage changes. As such we create a new model with a OneToOneField to our record for each type we plan on managing.

    When we query our records we wan to only worry about Records, so define our GraphQL types accordingly.

    class DiscoRecord(graphene_django.DjangoObjectType):
        class Meta:
            model = models.Record
        
        bpm = graphene.IntegerField(required=True)
    
        @classmethod
        def get_node(cls, info, id) -> models.Record:
            # Allow our object to be fetchable as a Relay Node
            return models.Record.objects.get(pk=id)
        
        def resolve_bpm(record: models.Record, **kwargs) -> int:
            return record.disco_record.bpm
    
    class PunkRecord(graphene_django.DjangoObjectType):
        class Meta:
            model = models.Record
        
        max_chords = graphene.IntegerField(required=True)
        
        @classmethod
        def get_node(cls, info, id) -> models.Record:
            # Allow our object to be fetchable as a Relay Node
            return models.Record.objects.get(pk=id)
        
        def resolve_max_chords(record: models.Record, **kwargs) -> int:
            return record.punk_record.max_chords
    
    
    class Record(graphene.Union):
        class Meta:
            types = (DiscoRecord, PunkRecord)
    
        @classmethod
        def resolve_type(
            cls, instance: models.Record, info
        ) -> Union[Type[DiscoRecord], Type[PunkRecord]]:
            # Graphene is unable to accurately determine which type it should resolve without help
            # because the unioned types are all DjangoObjectTypes for the same Record class.
            if instance.is_disco:
                return DiscoRecord
            elif instance.is_punk:
                return PunkRecord
            raise ValueError("Unknown record type")

    Because we have the resolve_type @classmethod defined in our Union, Graphene can correctly determine the record type. Without that we'd get an error any time we tried to resolve values that only exist on the PunkRecord or DiscoRecord type.

    So if we had a records query that returned our Record Union, we could query it as follows without any issues.

    query {
        records {
            ... on DiscoRecord {
                bpm
            }
            ... on PunkRecord {
                maxChords
            }
        }
    }

    But what about the Relay node query? The query looks quite similar to our records query.

    query {
        node(id: "fuga") {
            ... on DiscoRecord {
                bpm
            }
            ... on PunkRecord {
                maxChords
            }
        }
    }

    However, and this is the key difference, node does not return our Union type, but rather our individual DiscoRecord / PunkRecord type. And since both of those types are technically Record types (because of the same Django meta class), any PunkRecords will be resolved asΒ  DiscoRecords and return an error when we try to resolve Punk only fields.

    In order for node to be able to differentiate between the Punk and Disco at the type level we need one more is_type_of classmethod defined on our types.

    class DiscoRecord(graphene_django.DjangoObjectType):
        ...
        @classmethod
        def is_type_of(cls, root, info) -> bool:
            # When a DiscoRecord is resolved as a node it does not use the Union type
            # determine the object's type.
            # Only allow this type to be used with Disco records.
            if isinstance(root, models.Record):
                return root.is_disco
            return False
    
    class PunkRecord(graphene_django.DjangoObjectType):
        ...
        @classmethod
        def is_type_of(cls, root, info) -> bool:
            # When a PunkRecord is resolved as a node it does not use the Union type
            # to determine the object's type.
            # Only allow this type to be used with Punk records.
            if isinstance(root, models.Record):
                return root.is_punk
            return False

    This way, when Graphene is looping through all of our types trying to determine which type to use for a given Record, we can inspect the actual record and prevent an erroneous match.

    This is obvious in retrospect. Although our GraphQL query selectors are exactly the same the root type is different and as such requires a bit more instruction to resolve the appropriate type.
Previous 225 of 722 Next