MongoEngine / MongoEngine/mongoengine

The unique_with attribute enforces other fields to be required

Open
#1,242 6 comments 5 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Discussion
Dominant language
Python
Stars
4.3k
Forks
1.2k
Avg merge
4h 41m
Merged PRs (30d)
11

Description

BaseDocument._unique_with_indexes method looks like this:

    @classmethod
    def _unique_with_indexes(cls, namespace=""):
        """
        Find and set unique indexes
        """
        unique_indexes = []
        for field_name, field in list(cls._fields.items()):
            sparse = field.sparse
            # Generate a list of indexes needed by uniqueness constraints
            if field.unique:
                unique_fields = [field.db_field]

                # Add any unique_with fields to the back of the index spec
                if field.unique_with:
                    if isinstance(field.unique_with, str):
                        field.unique_with = [field.unique_with]

                    # Convert unique_with field names to real field names
                    unique_with = []
                    for other_name in field.unique_with:
                        parts = other_name.split('.')
                        # Lookup real name
                        parts = cls._lookup_field(parts)
                        name_parts = [part.db_field for part in parts]
                        unique_with.append('.'.join(name_parts))
                        # Unique field should be required
                        parts[-1].required = True
                        sparse = (not sparse and
                                  parts[-1].name not in cls.__dict__)
                    unique_fields += unique_with

                # Add the new index to the list
                fields = [("%s%s" % (namespace, f), pymongo.ASCENDING)
                          for f in unique_fields]
                index = {'fields': fields, 'unique': True, 'sparse': sparse}
                unique_indexes.append(index)

            if field.__class__.__name__ == "ListField":
                field = field.field

            # Grab any embedded document field unique indexes
            if (field.__class__.__name__ == "EmbeddedDocumentField" and
                    field.document_type != cls):
                field_namespace = "%s." % field_name
                doc_cls = field.document_type
                unique_indexes += doc_cls._unique_with_indexes(field_namespace)

        return unique_indexes

And I believe, this part shouldn't be so implicit:

# Unique field should be required
parts[-1].required = True

Because the following document won't work as expected, even though required=False is explicitly written:

class MongoDocument(Document):
    id1 = StringField(required=False, null=True)
    id2 = StringField(required=False, null=True, unique_with='id1')
  1. Why "Unique field should be required"? Unique indexes on nullable fields work like a charm.
  2. My proposal is as follows: either remove this logic completely or raise an exception if the field isn't declared as required explicitly:
if not parts[-1].required:
    raise ...

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at BaseDocument._unique_with_indexes and trace how required and sparse are applied to unique_with fields. Reproduce the MongoDocument example, then compare its behavior with MongoDB's nullable unique-index semantics and the issue's proposed alternatives. Done means the behavior is explicitly defined and verified with a regression test.

Written by the indexing model from the issue text.

Assessment

Tech stack
mongodb, python
Domain
backend, databases
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.