Request for Clarification and Guidance on the Status of Ticket #5929 Following Closure of Ticket #373

Hello,

I would like to seek clarification regarding ticket #5929, which has been marked as a duplicate of the recently closed ticket #373. My concern is whether the work we are doing on ticket #5929 remains relevant in light of the closure of ticket #373.

Our work on ticket #5929 focuses on developing a generalized MultiColumnField. To this point, we have successfully created this additional field, generated corresponding migrations, and established columns in the database following a specific naming convention. This effort aims to provide a general framework for MultiColumnField that can be adapted to various use cases, with IP address serving as an example application.

However, given that ticket #5929 has been identified as a duplicate of ticket #373, we would like to understand:

  1. Why was ticket #5929 identified as a duplicate of ticket #373? The objectives and approaches of both tickets seem similar but with different applications.
  2. Is it still relevant to continue work on ticket #5929, or should we shift our focus towards the discussions and solutions considered in ticket #373?
  3. Are there any specific directions or recommendations from the Django team to ensure that our contribution on ticket #5929 remains aligned with the overall goals of the Django project?

We are committed to making a meaningful contribution to Django and want to ensure that our efforts are in line with the needs of the community.

Thank you in advance for your feedback and guidance.

here are my advances on tiket #5929
an example of a model

from django.db import models
from django.utils import timezone

class Item(models.Model):
    name = models.CharField(max_length=100)
    last_updated = models.DateTimeField(auto_now=True)
    created_at = models.DateTimeField(auto_now_add=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    original_price = models.DecimalField(max_digits=10, decimal_places=2)
    stock = models.IntegerField()
    update_interval = models.DurationField(default=timezone.timedelta(days=1))


class UpsertConflict(models.Model):
    number = models.IntegerField(unique=True)
    rank = models.IntegerField()
    name = models.CharField(max_length=15)


class NetworkModel(models.Model):
    ip_address = models.MultiColumnField(sub_fields={
        'octet1': models.PositiveIntegerField(),
        'octet2': models.PositiveIntegerField(),
        'octet3': models.PositiveIntegerField(),
        'octet4': models.PositiveIntegerField()
    }, separator='__')

    def __str__(self):
        return f"{self.octet1}.{self.octet2}.{self.octet3}.{self.octet4}"

the implementation of the MultiColumnField class

class MultiColumnField(Field):
    def __init__(self, *args, **kwargs):
        self.separator = kwargs.pop("separator", ".")
        self.sub_fields = kwargs.pop("sub_fields", {})
        if not isinstance(self.sub_fields, dict):
            raise ValueError("sub_fields must be a dictionary.")

        super().__init__(*args, **kwargs)
        self.sub_field_names = {}  # Initialisé dans contribute_to_class

    def contribute_to_class(self, cls, name, private_only=False, **kwargs):
        super().contribute_to_class(cls, name, private_only=True, **kwargs)
        # Initialisation des sous-champs avec la convention de nommage
        for field_name, field in self.sub_fields.items():
            prefixed_field_name = f"{self.name}_{field_name}"
            self.sub_field_names[field_name] = prefixed_field_name
            field.set_attributes_from_name(prefixed_field_name)
            field.contribute_to_class(cls, prefixed_field_name)

    def get_prep_value(self, value):
        # Préparer les données pour l'insertion dans la base de données
        if isinstance(value, str):
            # Décomposer la valeur en utilisant le séparateur
            parts = value.split(self.separator)
            value = {
                field_name: part for field_name, part in zip(self.sub_fields, parts)
            }
        return {
            self.sub_field_names[field_name]: field.get_prep_value(value[field_name])
            for field_name, field in self.sub_fields.items()
        }

    def from_db_value(self, value, expression, connection):
        # Reconstruire la valeur Ă  partir des sous-champs
        reconstructed_value = self.separator.join(
            str(value[self.sub_field_names[field_name]])
            for field_name in self.sub_fields
        )
        return reconstructed_value

    def get_db_prep_value(self, value, connection, prepared=False):
        # Préparation des données pour la base de données
        return {
            self.sub_field_names[field_name]: field.get_db_prep_value(
                value[field_name], connection, prepared
            )
            for field_name, field in self.sub_fields.items()
        }

    """ def db_type(self, connection):
        # Indique à Django qu'il n'y a pas de colonne de base de données directement associée
        return None """
    
    def get_col(self, alias, output_field=None):
        # Retourner None ou une représentation adaptée pour le champ complexe
        return ''

migration generate

# Generated by Django 5.1.dev20231209152046 on 2023-12-10 21:15

from django.db import migrations, models


class Migration(migrations.Migration):

    dependencies = [
        ('myapp', '0002_upsertconflict'),
    ]

    operations = [
        migrations.CreateModel(
            name='NetworkModel',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('ip_address_octet1', models.PositiveIntegerField()),
                ('ip_address_octet2', models.PositiveIntegerField()),
                ('ip_address_octet3', models.PositiveIntegerField()),
                ('ip_address_octet4', models.PositiveIntegerField()),
            ],
        ),
    ]

my data base

We need compose fields to fix #373, and the proposed PR implements them in the first step. That’s why #5929 was marked as a duplicate.

Thank you for the clarification, @felixxm . However, I feel some frustration as I have spent time understanding and working upstream on ticket #5929. I haven’t submitted a pull request yet, but finding out midway that my efforts might not be needed due to the overlap with ticket #373 is somewhat disappointing…

@HamaBarhamou given no update at all was provided over 11 months you worked on the ticket I think it’s unfair to expect from Mariusz or anyone else doing triage of tickets to know you were still actively working on the ticket.

The good news is that your investment in understanding the problem are still very much needed to support @Lily-Foote work on the subject and we’d very happily take more folks to work on the problem.

In other words, the closing of #5929 should be seen as a way to consolidate efforts and enhance cooperation and less about discrediting your work so far.

2 Likes

Thank you @charettes for your understanding and support in consolidating efforts on ticket 373. I am still learning and progressing in my understanding of the technical aspects, but my willingness to contribute and collaborate with the team is strong. I have also taken a look at @Lily-Foote work and am impressed with the significant progress made. I appreciate the opportunity to be involved in this exciting project and am ready to offer my help in any way possible.

1 Like

Hello! I think this ticket (#5929) can be re-opened since it wasn’t really the focus of #373. It’s a bit unclear to me what this ticket is supposed to achieve in the first place. Maybe it can re-use the code written for #373. Or maybe not? I don’t know.

1 Like