diff --git a/docs/source/Coding/Changelog.md b/docs/source/Coding/Changelog.md index 0a8e5e399d..3287504a70 100644 --- a/docs/source/Coding/Changelog.md +++ b/docs/source/Coding/Changelog.md @@ -2,16 +2,40 @@ ## main branch -- [Feature] Add `evennia.ON_DEMAND_HANDLER` for making it easier to implement - timed element with the on-demand approach (Griatch) -- [Fix] Remove `AMP_ENABLED` setting since it services no real purpose and - erroring out on setting it would make it even less useful (Griatch). -- [Fix] `services` command with no args would traceback (regression) (Griatch) +- Feature: Add [`evennia.ON_DEMAND_HANDLER`][new-ondemandhandler] for making it + easier to implement changes that are calculated on-demand (Griatch) - [Feature][pull3412]: Make it possible to add custom webclient css in `webclient/css/custom.css`, same as for website (InspectorCaracal) +- [Feature][pull3367]: [Component contrib][pull3367extra] got better + inheritance, slot names to choose attr storage, speedups and fixes (ChrisLR) +- Feature: Break up `DefaultObject.search` method into several helpers to make + it easier to override (Griatch) +- Fix: Resolve multimatch error with rpsystem contrib (Griatch) +- Fix: Remove `AMP_ENABLED` setting since it services no real purpose and + erroring out on setting it would make it even less useful (Griatch). +- Feature: Remove too-strict password restrictions for Evennia logins, using + django defaults instead for passwords with more varied characters. +- Fix `services` command with no args would traceback (regression) (Griatch) +- [Fix][pull3423]: Fix wilderness contrib error moving to an already existing + wilderness room (InspectorCaracal) +- [Fix][pull3425]: Don't always include example the crafting recipe when + using the crafting contrib (InspectorCaracal) +- [Fix][pull3426]: Traceback banning a channel using with only one nick + (InspectorCaracal) +- [Fix][pull3434]: Adjust lunr search weights to void clashing of cmd-aliases over + keys which caused some help entries to shadow others (InspectorCaracal) +- Fix: Make `menu/email_login` contribs honor `NEW_ACCOUNT_REGISTRATION_ENABLED` + setting (Griatch) - Doc fixes (InspectorCaracal, Griatch) +[new-ondemandhandler][https://www.evennia.com/docs/latest/Components/OnDemandHandler.html] [pull3412]: https://github.com/evennia/evennia/pull/3412 +[pull3423]: https://github.com/evennia/evennia/pull/3423 +[pull3425]: https://github.com/evennia/evennia/pull/3425 +[pull3426]: https://github.com/evennia/evennia/pull/3426 +[pull3434]: https://github.com/evennia/evennia/pull/3434 +[pull3367]: https://github.com/evennia/evennia/pull/3367 +[pull3367extra]: https://www.evennia.com/docs/latest/Contribs/Contrib-Components.html ## Evennia 3.1.1 diff --git a/docs/source/Components/Attributes.md b/docs/source/Components/Attributes.md index e971b18b4b..f6ce8ed700 100644 --- a/docs/source/Components/Attributes.md +++ b/docs/source/Components/Attributes.md @@ -294,6 +294,20 @@ A lock is no good if nothing checks it -- and by default Evennia does not check The same keywords are available to use with `obj.attributes.set()` and `obj.attributes.remove()`, those will check for the `attredit` lock type. +## Querying by Attribute + +While you can get attributes using the `obj.attributes.get` handler, you can also find objects based on the Attributes they have through the `db_attributes` many-to-many field available on each typeclassed entity: + +```python +# find objects by attribue assigned (regardless of value) +objs = evennia.ObjectDB.objects.filter(db_attributes__db_key="foo") +# find objects with attribute of particular value assigned to them +objs = evennia.ObjectDB.objects.filter(db_attributes__db_key="foo", db_attributes__db_value="bar") +``` + +```{important} +Internally, Attribute values are stored as _pickled strings_ (see next section). When querying, your search string is converted to the same format and matched in that form. While this means Attributes can store arbitrary Python structures, the drawback is that you cannot do more advanced database comparisons on them. For example doing `db_attributes__db__value__lt=4` or `__gt=0` will not work since less-than and greater-than doesn't do what you want between strings. +``` ## What types of data can I save in an Attribute? diff --git a/docs/source/Contribs/Contrib-Components.md b/docs/source/Contribs/Contrib-Components.md index 2e12a11cef..521d10c44a 100644 --- a/docs/source/Contribs/Contrib-Components.md +++ b/docs/source/Contribs/Contrib-Components.md @@ -30,12 +30,20 @@ class Character(ComponentHolderMixin, DefaultCharacter): # ... ``` -Components need to inherit the Component class directly and require a name. +Components need to inherit the Component class and require a unique name. +Components may inherit from other components but must specify another name. +You can assign the same 'slot' to both components to have alternative implementations. ```python from evennia.contrib.base_systems.components import Component + class Health(Component): name = "health" + + +class ItemHealth(Health): + name = "item_health" + slot = "health" ``` Components may define DBFields or NDBFields at the class level. @@ -103,7 +111,10 @@ character.components.add(vampirism) ... -vampirism_from_elsewhere = character.components.get("vampirism") +vampirism = character.components.get("vampirism") + +# Alternatively +vampirism = character.cmp.vampirism ``` Keep in mind that all components must be imported to be visible in the listing. @@ -128,6 +139,14 @@ from typeclasses.components import health ``` Both of the above examples will work. +## Known Issues + +Assigning mutable default values such as a list to a DBField will share it across instances. +To avoid this, you must set autocreate=True on the field, like this. +```python +health = DBField(default=[], autocreate=True) +``` + ## Full Example ```python from evennia.contrib.base_systems import components diff --git a/docs/source/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Django-queries.md b/docs/source/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Django-queries.md index 031ae73298..d13c631cc8 100644 --- a/docs/source/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Django-queries.md +++ b/docs/source/Howtos/Beginner-Tutorial/Part1/Beginner-Tutorial-Django-queries.md @@ -98,8 +98,7 @@ found. ## Queryset field lookups -Above we found roses with exactly the `db_key` `"rose"`. This is an _exact_ match that is _case sensitive_, -so it would not find `"Rose"`. +Above we found roses with exactly the `db_key` `"rose"`. This is an _exact_ match that is _case sensitive_, so it would not find `"Rose"`. ```python # this is case-sensitive and the same as = @@ -108,15 +107,13 @@ roses = Flower.objects.filter(db_key__exact="rose" # the i means it's case-insensitive roses = Flower.objects.filter(db_key__iexact="rose") ``` -The Django field query language uses `__` similarly to how Python uses `.` to access resources. This -is because `.` is not allowed in a function keyword. +The Django field query language uses `__` similarly to how Python uses `.` to access resources. This is because `.` is not allowed in a function keyword. ```python roses = Flower.objects.filter(db_key__icontains="rose") ``` -This will find all flowers whose name contains the string `"rose"`, like `"roses"`, `"wild rose"` etc. The `i` in the beginning makes the search case-insensitive. Other useful variations to use -are `__istartswith` and `__iendswith`. You can also use `__gt`, `__ge` for "greater-than"/"greater-or-equal-than" comparisons (same for `__lt` and `__le`). There is also `__in`: +This will find all flowers whose name contains the string `"rose"`, like `"roses"`, `"wild rose"` etc. The `i` in the beginning makes the search case-insensitive. Other useful variations to use are `__istartswith` and `__iendswith`. You can also use `__gt`, `__ge` for "greater-than"/"greater-or-equal-than" comparisons (same for `__lt` and `__le`). There is also `__in`: ```python swords = Weapons.objects.filter(db_key__in=("rapier", "two-hander", "shortsword")) @@ -178,7 +175,7 @@ will_transform = ( .filter( db_location__db_tags__db_key__iexact="moonlit", db_attributes__db_key="lycantrophy", - db_attributes__db_value__gt=2 + db_attributes__db_value__eq=2 ) ) ``` @@ -193,10 +190,13 @@ Don't confuse database fields with [Attributes](../../../Components/Attributes.m that we can treat like an object for this purpose; it references all Tags on the location) - ... and from those `Tags`, we looking for `Tags` whose `db_key` is "monlit" (non-case sensitive). - **Line 7**: ... We also want only Characters with `Attributes` whose `db_key` is exactly `"lycantrophy"` - - **Line 8** :... at the same time as the `Attribute`'s `db_value` is greater-than 2. + - **Line 8** :... at the same time as the `Attribute`'s `db_value` is exactly 2. -Running this query makes our newly lycantrophic Character appear in `will_transform` so we -know to transform it. Success! +Running this query makes our newly lycantrophic Character appear in `will_transform` so we know to transform it. Success! + +```{important} +You can't query for an Attribute `db_value` quite as freely as other data-types. This is because Attributes can store any Python entity and is actually stored as _strings_ on the database side. So while you can use `__eq=2` in the above example, you will not be able to `__gt=2` or `__lt=2` because these operations don't make sense for strings. See [Attributes](../../../Components/Attributes.md#querying-by-attribute) for more information on dealing with Attributes. +``` ## Queries with OR or NOT @@ -243,7 +243,7 @@ will_transform = ( Q(db_location__db_tags__db_key__iexact="moonlit") & ( Q(db_attributes__db_key="lycantrophy", - db_attributes__db_value__gt=2) + db_attributes__db_value__eq=2) | Q(db_tags__db_key__iexact="recently_bitten") )) .distinct() @@ -256,7 +256,7 @@ That's quite compact. It may be easier to see what's going on if written this wa from django.db.models import Q q_moonlit = Q(db_location__db_tags__db_key__iexact="moonlit") -q_lycantropic = Q(db_attributes__db_key="lycantrophy", db_attributes__db_value__gt=2) +q_lycantropic = Q(db_attributes__db_key="lycantrophy", db_attributes__db_value__eq=2) q_recently_bitten = Q(db_tags__db_key__iexact="recently_bitten") will_transform = ( @@ -362,9 +362,7 @@ result = ( ) ``` -Here we used `.annotate` to create two in-query 'variables' `num_objects` and `num_tags`. We then -directly use these results in the filter. Using `F()` allows for also the right-hand-side of the filter -condition to be calculated on the fly, completely within the database. +Here we used `.annotate` to create two in-query 'variables' `num_objects` and `num_tags`. We then directly use these results in the filter. Using `F()` allows for also the right-hand-side of the filter condition to be calculated on the fly, completely within the database. ## Grouping and returning only certain properties diff --git a/docs/source/Setup/Settings-Default.md b/docs/source/Setup/Settings-Default.md index 5535899c02..a52729f166 100644 --- a/docs/source/Setup/Settings-Default.md +++ b/docs/source/Setup/Settings-Default.md @@ -1115,7 +1115,6 @@ AUTH_PASSWORD_VALIDATORS = [ }, {"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"}, {"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"}, - {"NAME": "evennia.server.validators.EvenniaPasswordValidator"}, ] # Username validation plugins diff --git a/docs/source/api/evennia.contrib.base_systems.components.exceptions.md b/docs/source/api/evennia.contrib.base_systems.components.exceptions.md new file mode 100644 index 0000000000..e249723199 --- /dev/null +++ b/docs/source/api/evennia.contrib.base_systems.components.exceptions.md @@ -0,0 +1,10 @@ +```{eval-rst} +evennia.contrib.base\_systems.components.exceptions +========================================================== + +.. automodule:: evennia.contrib.base_systems.components.exceptions + :members: + :undoc-members: + :show-inheritance: + +``` \ No newline at end of file diff --git a/docs/source/api/evennia.contrib.base_systems.components.listing.md b/docs/source/api/evennia.contrib.base_systems.components.listing.md new file mode 100644 index 0000000000..1c7d77f5e9 --- /dev/null +++ b/docs/source/api/evennia.contrib.base_systems.components.listing.md @@ -0,0 +1,10 @@ +```{eval-rst} +evennia.contrib.base\_systems.components.listing +======================================================= + +.. automodule:: evennia.contrib.base_systems.components.listing + :members: + :undoc-members: + :show-inheritance: + +``` \ No newline at end of file diff --git a/docs/source/api/evennia.contrib.base_systems.components.md b/docs/source/api/evennia.contrib.base_systems.components.md index c72406ab4d..e647b78e37 100644 --- a/docs/source/api/evennia.contrib.base_systems.components.md +++ b/docs/source/api/evennia.contrib.base_systems.components.md @@ -14,7 +14,9 @@ evennia.contrib.base\_systems.components evennia.contrib.base_systems.components.component evennia.contrib.base_systems.components.dbfield + evennia.contrib.base_systems.components.exceptions evennia.contrib.base_systems.components.holder + evennia.contrib.base_systems.components.listing evennia.contrib.base_systems.components.signals evennia.contrib.base_systems.components.tests